xref: /aosp_15_r20/external/skia/src/sksl/codegen/SkSLRasterPipelineCodeGenerator.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2022 Google LLC
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/SkSLRasterPipelineCodeGenerator.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSpan.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTArray.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkEnumBitMask.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkStringView.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkUtils.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTHash.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLAnalysis.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLBuiltinTypes.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLCompiler.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLConstantFolder.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLContext.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLDefines.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLIntrinsicList.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLOperator.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLPosition.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/analysis/SkSLProgramUsage.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/codegen/SkSLRasterPipelineBuilder.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLBinaryExpression.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLBlock.h"
31*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLBreakStatement.h"
32*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLChildCall.h"
33*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructor.h"
34*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
35*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructorMatrixResize.h"
36*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructorSplat.h"
37*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLContinueStatement.h"
38*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLDoStatement.h"
39*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLExpression.h"
40*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLExpressionStatement.h"
41*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFieldAccess.h"
42*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLForStatement.h"
43*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFunctionCall.h"
44*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFunctionDeclaration.h"
45*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFunctionDefinition.h"
46*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLIRNode.h"
47*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLIfStatement.h"
48*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLIndexExpression.h"
49*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLLayout.h"
50*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLLiteral.h"
51*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLModifierFlags.h"
52*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLPostfixExpression.h"
53*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLPrefixExpression.h"
54*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLProgram.h"
55*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLProgramElement.h"
56*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLReturnStatement.h"
57*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLStatement.h"
58*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSwitchCase.h"
59*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSwitchStatement.h"
60*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSwizzle.h"
61*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLTernaryExpression.h"
62*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLType.h"
63*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLVarDeclarations.h"
64*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLVariable.h"
65*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLVariableReference.h"
66*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/tracing/SkSLDebugTracePriv.h"
67*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/transform/SkSLTransform.h"
68*c8dee2aaSAndroid Build Coastguard Worker 
69*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
70*c8dee2aaSAndroid Build Coastguard Worker #include <climits>
71*c8dee2aaSAndroid Build Coastguard Worker #include <cstddef>
72*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
73*c8dee2aaSAndroid Build Coastguard Worker #include <float.h>
74*c8dee2aaSAndroid Build Coastguard Worker #include <iterator>
75*c8dee2aaSAndroid Build Coastguard Worker #include <optional>
76*c8dee2aaSAndroid Build Coastguard Worker #include <string>
77*c8dee2aaSAndroid Build Coastguard Worker #include <string_view>
78*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
79*c8dee2aaSAndroid Build Coastguard Worker #include <vector>
80*c8dee2aaSAndroid Build Coastguard Worker 
81*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
82*c8dee2aaSAndroid Build Coastguard Worker 
83*c8dee2aaSAndroid Build Coastguard Worker namespace SkSL {
84*c8dee2aaSAndroid Build Coastguard Worker namespace RP {
85*c8dee2aaSAndroid Build Coastguard Worker 
unsupported()86*c8dee2aaSAndroid Build Coastguard Worker static bool unsupported() {
87*c8dee2aaSAndroid Build Coastguard Worker     // If MakeRasterPipelineProgram returns false, set a breakpoint here for more information.
88*c8dee2aaSAndroid Build Coastguard Worker     return false;
89*c8dee2aaSAndroid Build Coastguard Worker }
90*c8dee2aaSAndroid Build Coastguard Worker 
91*c8dee2aaSAndroid Build Coastguard Worker class AutoContinueMask;
92*c8dee2aaSAndroid Build Coastguard Worker class Generator;
93*c8dee2aaSAndroid Build Coastguard Worker class LValue;
94*c8dee2aaSAndroid Build Coastguard Worker 
95*c8dee2aaSAndroid Build Coastguard Worker class SlotManager {
96*c8dee2aaSAndroid Build Coastguard Worker public:
SlotManager(std::vector<SlotDebugInfo> * i)97*c8dee2aaSAndroid Build Coastguard Worker     SlotManager(std::vector<SlotDebugInfo>* i) : fSlotDebugInfo(i) {}
98*c8dee2aaSAndroid Build Coastguard Worker 
99*c8dee2aaSAndroid Build Coastguard Worker     /** Used by `createSlots` to add this variable to SlotDebugInfo inside the DebugTrace. */
100*c8dee2aaSAndroid Build Coastguard Worker     void addSlotDebugInfoForGroup(const std::string& varName,
101*c8dee2aaSAndroid Build Coastguard Worker                                   const Type& type,
102*c8dee2aaSAndroid Build Coastguard Worker                                   Position pos,
103*c8dee2aaSAndroid Build Coastguard Worker                                   int* groupIndex,
104*c8dee2aaSAndroid Build Coastguard Worker                                   bool isFunctionReturnValue);
105*c8dee2aaSAndroid Build Coastguard Worker     void addSlotDebugInfo(const std::string& varName,
106*c8dee2aaSAndroid Build Coastguard Worker                           const Type& type,
107*c8dee2aaSAndroid Build Coastguard Worker                           Position pos,
108*c8dee2aaSAndroid Build Coastguard Worker                           bool isFunctionReturnValue);
109*c8dee2aaSAndroid Build Coastguard Worker 
110*c8dee2aaSAndroid Build Coastguard Worker     /** Creates slots associated with an SkSL variable or return value. */
111*c8dee2aaSAndroid Build Coastguard Worker     SlotRange createSlots(std::string name,
112*c8dee2aaSAndroid Build Coastguard Worker                           const Type& type,
113*c8dee2aaSAndroid Build Coastguard Worker                           Position pos,
114*c8dee2aaSAndroid Build Coastguard Worker                           bool isFunctionReturnValue);
115*c8dee2aaSAndroid Build Coastguard Worker 
116*c8dee2aaSAndroid Build Coastguard Worker     /**
117*c8dee2aaSAndroid Build Coastguard Worker      * Associates previously-created slots with an SkSL variable; this can allow multiple variables
118*c8dee2aaSAndroid Build Coastguard Worker      * to share overlapping ranges. If the variable was already associated with a slot range,
119*c8dee2aaSAndroid Build Coastguard Worker      * returns the previously associated range.
120*c8dee2aaSAndroid Build Coastguard Worker      */
121*c8dee2aaSAndroid Build Coastguard Worker     std::optional<SlotRange> mapVariableToSlots(const Variable& v, SlotRange range);
122*c8dee2aaSAndroid Build Coastguard Worker 
123*c8dee2aaSAndroid Build Coastguard Worker     /**
124*c8dee2aaSAndroid Build Coastguard Worker      * Deletes the existing mapping between a variable and its slots; a future call to
125*c8dee2aaSAndroid Build Coastguard Worker      * `getVariableSlots` will see this as a brand new variable and associate new slots.
126*c8dee2aaSAndroid Build Coastguard Worker      */
127*c8dee2aaSAndroid Build Coastguard Worker     void unmapVariableSlots(const Variable& v);
128*c8dee2aaSAndroid Build Coastguard Worker 
129*c8dee2aaSAndroid Build Coastguard Worker     /** Looks up the slots associated with an SkSL variable; creates the slot if necessary. */
130*c8dee2aaSAndroid Build Coastguard Worker     SlotRange getVariableSlots(const Variable& v);
131*c8dee2aaSAndroid Build Coastguard Worker 
132*c8dee2aaSAndroid Build Coastguard Worker     /**
133*c8dee2aaSAndroid Build Coastguard Worker      * Looks up the slots associated with an SkSL function's return value; creates the range if
134*c8dee2aaSAndroid Build Coastguard Worker      * necessary. Note that recursion is never supported, so we don't need to maintain return values
135*c8dee2aaSAndroid Build Coastguard Worker      * in a stack; we can just statically allocate one slot per function call-site.
136*c8dee2aaSAndroid Build Coastguard Worker      */
137*c8dee2aaSAndroid Build Coastguard Worker     SlotRange getFunctionSlots(const IRNode& callSite, const FunctionDeclaration& f);
138*c8dee2aaSAndroid Build Coastguard Worker 
139*c8dee2aaSAndroid Build Coastguard Worker     /** Returns the total number of slots consumed. */
slotCount() const140*c8dee2aaSAndroid Build Coastguard Worker     int slotCount() const { return fSlotCount; }
141*c8dee2aaSAndroid Build Coastguard Worker 
142*c8dee2aaSAndroid Build Coastguard Worker private:
143*c8dee2aaSAndroid Build Coastguard Worker     THashMap<const IRNode*, SlotRange> fSlotMap;
144*c8dee2aaSAndroid Build Coastguard Worker     int fSlotCount = 0;
145*c8dee2aaSAndroid Build Coastguard Worker     std::vector<SlotDebugInfo>* fSlotDebugInfo;
146*c8dee2aaSAndroid Build Coastguard Worker };
147*c8dee2aaSAndroid Build Coastguard Worker 
148*c8dee2aaSAndroid Build Coastguard Worker class AutoStack {
149*c8dee2aaSAndroid Build Coastguard Worker public:
150*c8dee2aaSAndroid Build Coastguard Worker     /**
151*c8dee2aaSAndroid Build Coastguard Worker      * Creates a temporary stack. The caller is responsible for discarding every entry on this
152*c8dee2aaSAndroid Build Coastguard Worker      * stack before ~AutoStack is reached.
153*c8dee2aaSAndroid Build Coastguard Worker      */
154*c8dee2aaSAndroid Build Coastguard Worker     explicit AutoStack(Generator* g);
155*c8dee2aaSAndroid Build Coastguard Worker     ~AutoStack();
156*c8dee2aaSAndroid Build Coastguard Worker 
157*c8dee2aaSAndroid Build Coastguard Worker     /** Activates the associated stack. */
158*c8dee2aaSAndroid Build Coastguard Worker     void enter();
159*c8dee2aaSAndroid Build Coastguard Worker 
160*c8dee2aaSAndroid Build Coastguard Worker     /** Undoes a call to `enter`, returning to the previously-active stack. */
161*c8dee2aaSAndroid Build Coastguard Worker     void exit();
162*c8dee2aaSAndroid Build Coastguard Worker 
163*c8dee2aaSAndroid Build Coastguard Worker     /** Returns the stack ID of this AutoStack. */
stackID()164*c8dee2aaSAndroid Build Coastguard Worker     int stackID() { return fStackID; }
165*c8dee2aaSAndroid Build Coastguard Worker 
166*c8dee2aaSAndroid Build Coastguard Worker     /** Clones values from this stack onto the top of the active stack. */
167*c8dee2aaSAndroid Build Coastguard Worker     void pushClone(int slots);
168*c8dee2aaSAndroid Build Coastguard Worker 
169*c8dee2aaSAndroid Build Coastguard Worker     /** Clones values from a fixed range of this stack onto the top of the active stack. */
170*c8dee2aaSAndroid Build Coastguard Worker     void pushClone(SlotRange range, int offsetFromStackTop);
171*c8dee2aaSAndroid Build Coastguard Worker 
172*c8dee2aaSAndroid Build Coastguard Worker     /** Clones values from a dynamic range of this stack onto the top of the active stack. */
173*c8dee2aaSAndroid Build Coastguard Worker     void pushCloneIndirect(SlotRange range, int dynamicStackID, int offsetFromStackTop);
174*c8dee2aaSAndroid Build Coastguard Worker 
175*c8dee2aaSAndroid Build Coastguard Worker private:
176*c8dee2aaSAndroid Build Coastguard Worker     Generator* fGenerator;
177*c8dee2aaSAndroid Build Coastguard Worker     int fStackID = 0;
178*c8dee2aaSAndroid Build Coastguard Worker     int fParentStackID = 0;
179*c8dee2aaSAndroid Build Coastguard Worker };
180*c8dee2aaSAndroid Build Coastguard Worker 
181*c8dee2aaSAndroid Build Coastguard Worker class Generator {
182*c8dee2aaSAndroid Build Coastguard Worker public:
Generator(const SkSL::Program & program,DebugTracePriv * debugTrace,bool writeTraceOps)183*c8dee2aaSAndroid Build Coastguard Worker     Generator(const SkSL::Program& program, DebugTracePriv* debugTrace, bool writeTraceOps)
184*c8dee2aaSAndroid Build Coastguard Worker             : fProgram(program)
185*c8dee2aaSAndroid Build Coastguard Worker             , fContext(fProgram.fContext->fTypes, *fProgram.fContext->fErrors)
186*c8dee2aaSAndroid Build Coastguard Worker             , fDebugTrace(debugTrace)
187*c8dee2aaSAndroid Build Coastguard Worker             , fWriteTraceOps(writeTraceOps)
188*c8dee2aaSAndroid Build Coastguard Worker             , fProgramSlots(debugTrace ? &debugTrace->fSlotInfo : nullptr)
189*c8dee2aaSAndroid Build Coastguard Worker             , fUniformSlots(debugTrace ? &debugTrace->fUniformInfo : nullptr)
190*c8dee2aaSAndroid Build Coastguard Worker             , fImmutableSlots(nullptr) {
191*c8dee2aaSAndroid Build Coastguard Worker         fContext.fConfig = fProgram.fConfig.get();
192*c8dee2aaSAndroid Build Coastguard Worker         fContext.fModule = fProgram.fContext->fModule;
193*c8dee2aaSAndroid Build Coastguard Worker     }
194*c8dee2aaSAndroid Build Coastguard Worker 
~Generator()195*c8dee2aaSAndroid Build Coastguard Worker     ~Generator() {
196*c8dee2aaSAndroid Build Coastguard Worker         // ~AutoStack calls into the Generator, so we need to make sure the trace mask is reset
197*c8dee2aaSAndroid Build Coastguard Worker         // before the Generator is destroyed.
198*c8dee2aaSAndroid Build Coastguard Worker         fTraceMask.reset();
199*c8dee2aaSAndroid Build Coastguard Worker     }
200*c8dee2aaSAndroid Build Coastguard Worker 
201*c8dee2aaSAndroid Build Coastguard Worker     /** Converts the SkSL main() function into a set of Instructions. */
202*c8dee2aaSAndroid Build Coastguard Worker     bool writeProgram(const FunctionDefinition& function);
203*c8dee2aaSAndroid Build Coastguard Worker 
204*c8dee2aaSAndroid Build Coastguard Worker     /** Returns the generated program. */
205*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<RP::Program> finish();
206*c8dee2aaSAndroid Build Coastguard Worker 
207*c8dee2aaSAndroid Build Coastguard Worker     /**
208*c8dee2aaSAndroid Build Coastguard Worker      * Converts an SkSL function into a set of Instructions. Returns nullopt if the function
209*c8dee2aaSAndroid Build Coastguard Worker      * contained unsupported statements or expressions.
210*c8dee2aaSAndroid Build Coastguard Worker      */
211*c8dee2aaSAndroid Build Coastguard Worker     std::optional<SlotRange> writeFunction(const IRNode& callSite,
212*c8dee2aaSAndroid Build Coastguard Worker                                            const FunctionDefinition& function,
213*c8dee2aaSAndroid Build Coastguard Worker                                            SkSpan<std::unique_ptr<Expression> const> arguments);
214*c8dee2aaSAndroid Build Coastguard Worker 
215*c8dee2aaSAndroid Build Coastguard Worker     /**
216*c8dee2aaSAndroid Build Coastguard Worker      * Returns the slot index of this function inside the FunctionDebugInfo array in DebugTracePriv.
217*c8dee2aaSAndroid Build Coastguard Worker      * The FunctionDebugInfo slot will be created if it doesn't already exist.
218*c8dee2aaSAndroid Build Coastguard Worker      */
219*c8dee2aaSAndroid Build Coastguard Worker     int getFunctionDebugInfo(const FunctionDeclaration& decl);
220*c8dee2aaSAndroid Build Coastguard Worker 
221*c8dee2aaSAndroid Build Coastguard Worker     /** Returns true for variables with slots in fProgramSlots; immutables or uniforms are false. */
hasVariableSlots(const Variable & v)222*c8dee2aaSAndroid Build Coastguard Worker     bool hasVariableSlots(const Variable& v) {
223*c8dee2aaSAndroid Build Coastguard Worker         return !IsUniform(v) && !fImmutableVariables.contains(&v);
224*c8dee2aaSAndroid Build Coastguard Worker     }
225*c8dee2aaSAndroid Build Coastguard Worker 
226*c8dee2aaSAndroid Build Coastguard Worker     /** Looks up the slots associated with an SkSL variable; creates the slots if necessary. */
getVariableSlots(const Variable & v)227*c8dee2aaSAndroid Build Coastguard Worker     SlotRange getVariableSlots(const Variable& v) {
228*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(this->hasVariableSlots(v));
229*c8dee2aaSAndroid Build Coastguard Worker         return fProgramSlots.getVariableSlots(v);
230*c8dee2aaSAndroid Build Coastguard Worker     }
231*c8dee2aaSAndroid Build Coastguard Worker 
232*c8dee2aaSAndroid Build Coastguard Worker     /**
233*c8dee2aaSAndroid Build Coastguard Worker      * Looks up the slots associated with an immutable variable; creates the slots if necessary.
234*c8dee2aaSAndroid Build Coastguard Worker      */
getImmutableSlots(const Variable & v)235*c8dee2aaSAndroid Build Coastguard Worker     SlotRange getImmutableSlots(const Variable& v) {
236*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!IsUniform(v));
237*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fImmutableVariables.contains(&v));
238*c8dee2aaSAndroid Build Coastguard Worker         return fImmutableSlots.getVariableSlots(v);
239*c8dee2aaSAndroid Build Coastguard Worker     }
240*c8dee2aaSAndroid Build Coastguard Worker 
241*c8dee2aaSAndroid Build Coastguard Worker     /** Looks up the slots associated with an SkSL uniform; creates the slots if necessary. */
getUniformSlots(const Variable & v)242*c8dee2aaSAndroid Build Coastguard Worker     SlotRange getUniformSlots(const Variable& v) {
243*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(IsUniform(v));
244*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!fImmutableVariables.contains(&v));
245*c8dee2aaSAndroid Build Coastguard Worker         return fUniformSlots.getVariableSlots(v);
246*c8dee2aaSAndroid Build Coastguard Worker     }
247*c8dee2aaSAndroid Build Coastguard Worker 
248*c8dee2aaSAndroid Build Coastguard Worker     /**
249*c8dee2aaSAndroid Build Coastguard Worker      * Looks up the slots associated with an SkSL function's return value; creates the range if
250*c8dee2aaSAndroid Build Coastguard Worker      * necessary. Note that recursion is never supported, so we don't need to maintain return values
251*c8dee2aaSAndroid Build Coastguard Worker      * in a stack; we can just statically allocate one slot per function call-site.
252*c8dee2aaSAndroid Build Coastguard Worker      */
getFunctionSlots(const IRNode & callSite,const FunctionDeclaration & f)253*c8dee2aaSAndroid Build Coastguard Worker     SlotRange getFunctionSlots(const IRNode& callSite, const FunctionDeclaration& f) {
254*c8dee2aaSAndroid Build Coastguard Worker         return fProgramSlots.getFunctionSlots(callSite, f);
255*c8dee2aaSAndroid Build Coastguard Worker     }
256*c8dee2aaSAndroid Build Coastguard Worker 
257*c8dee2aaSAndroid Build Coastguard Worker     /**
258*c8dee2aaSAndroid Build Coastguard Worker      * Creates an additional stack for the program to push values onto. The stack will not become
259*c8dee2aaSAndroid Build Coastguard Worker      * actively in-use until `setCurrentStack` is called.
260*c8dee2aaSAndroid Build Coastguard Worker      */
261*c8dee2aaSAndroid Build Coastguard Worker     int createStack();
262*c8dee2aaSAndroid Build Coastguard Worker 
263*c8dee2aaSAndroid Build Coastguard Worker     /** Frees a stack generated by `createStack`. The freed stack must be completely empty. */
264*c8dee2aaSAndroid Build Coastguard Worker     void recycleStack(int stackID);
265*c8dee2aaSAndroid Build Coastguard Worker 
266*c8dee2aaSAndroid Build Coastguard Worker     /** Redirects builder ops to point to a different stack (created by `createStack`). */
267*c8dee2aaSAndroid Build Coastguard Worker     void setCurrentStack(int stackID);
268*c8dee2aaSAndroid Build Coastguard Worker 
269*c8dee2aaSAndroid Build Coastguard Worker     /** Reports the currently active stack. */
currentStack()270*c8dee2aaSAndroid Build Coastguard Worker     int currentStack() {
271*c8dee2aaSAndroid Build Coastguard Worker         return fCurrentStack;
272*c8dee2aaSAndroid Build Coastguard Worker     }
273*c8dee2aaSAndroid Build Coastguard Worker 
274*c8dee2aaSAndroid Build Coastguard Worker     /**
275*c8dee2aaSAndroid Build Coastguard Worker      * Returns an LValue for the passed-in expression; if the expression isn't supported as an
276*c8dee2aaSAndroid Build Coastguard Worker      * LValue, returns nullptr.
277*c8dee2aaSAndroid Build Coastguard Worker      */
278*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<LValue> makeLValue(const Expression& e, bool allowScratch = false);
279*c8dee2aaSAndroid Build Coastguard Worker 
280*c8dee2aaSAndroid Build Coastguard Worker     /** Copies the top-of-stack value into this lvalue, without discarding it from the stack. */
281*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool store(LValue& lvalue);
282*c8dee2aaSAndroid Build Coastguard Worker 
283*c8dee2aaSAndroid Build Coastguard Worker     /** Pushes the lvalue onto the top-of-stack. */
284*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool push(LValue& lvalue);
285*c8dee2aaSAndroid Build Coastguard Worker 
286*c8dee2aaSAndroid Build Coastguard Worker     /** The Builder stitches our instructions together into Raster Pipeline code. */
builder()287*c8dee2aaSAndroid Build Coastguard Worker     Builder* builder() { return &fBuilder; }
288*c8dee2aaSAndroid Build Coastguard Worker 
289*c8dee2aaSAndroid Build Coastguard Worker     /** Appends a statement to the program. */
290*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool writeStatement(const Statement& s);
291*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool writeBlock(const Block& b);
292*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool writeBreakStatement(const BreakStatement& b);
293*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool writeContinueStatement(const ContinueStatement& b);
294*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool writeDoStatement(const DoStatement& d);
295*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool writeExpressionStatement(const ExpressionStatement& e);
296*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool writeMasklessForStatement(const ForStatement& f);
297*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool writeForStatement(const ForStatement& f);
298*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool writeGlobals();
299*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool writeIfStatement(const IfStatement& i);
300*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool writeDynamicallyUniformIfStatement(const IfStatement& i);
301*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool writeReturnStatement(const ReturnStatement& r);
302*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool writeSwitchStatement(const SwitchStatement& s);
303*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool writeVarDeclaration(const VarDeclaration& v);
304*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool writeImmutableVarDeclaration(const VarDeclaration& d);
305*c8dee2aaSAndroid Build Coastguard Worker 
306*c8dee2aaSAndroid Build Coastguard Worker     /** Pushes an expression to the value stack. */
307*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushBinaryExpression(const BinaryExpression& e);
308*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushBinaryExpression(const Expression& left,
309*c8dee2aaSAndroid Build Coastguard Worker                                             Operator op,
310*c8dee2aaSAndroid Build Coastguard Worker                                             const Expression& right);
311*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushChildCall(const ChildCall& c);
312*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushConstructorCast(const AnyConstructor& c);
313*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushConstructorCompound(const AnyConstructor& c);
314*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushConstructorDiagonalMatrix(const ConstructorDiagonalMatrix& c);
315*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushConstructorMatrixResize(const ConstructorMatrixResize& c);
316*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushConstructorSplat(const ConstructorSplat& c);
317*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushExpression(const Expression& e, bool usesResult = true);
318*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushFieldAccess(const FieldAccess& f);
319*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushFunctionCall(const FunctionCall& c);
320*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushIndexExpression(const IndexExpression& i);
321*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushIntrinsic(const FunctionCall& c);
322*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushIntrinsic(IntrinsicKind intrinsic, const Expression& arg0);
323*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushIntrinsic(IntrinsicKind intrinsic,
324*c8dee2aaSAndroid Build Coastguard Worker                                      const Expression& arg0,
325*c8dee2aaSAndroid Build Coastguard Worker                                      const Expression& arg1);
326*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushIntrinsic(IntrinsicKind intrinsic,
327*c8dee2aaSAndroid Build Coastguard Worker                                      const Expression& arg0,
328*c8dee2aaSAndroid Build Coastguard Worker                                      const Expression& arg1,
329*c8dee2aaSAndroid Build Coastguard Worker                                      const Expression& arg2);
330*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushLiteral(const Literal& l);
331*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushPostfixExpression(const PostfixExpression& p, bool usesResult);
332*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushPrefixExpression(const PrefixExpression& p);
333*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushPrefixExpression(Operator op, const Expression& expr);
334*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushSwizzle(const Swizzle& s);
335*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushTernaryExpression(const TernaryExpression& t);
336*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushTernaryExpression(const Expression& test,
337*c8dee2aaSAndroid Build Coastguard Worker                                              const Expression& ifTrue,
338*c8dee2aaSAndroid Build Coastguard Worker                                              const Expression& ifFalse);
339*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushDynamicallyUniformTernaryExpression(const Expression& test,
340*c8dee2aaSAndroid Build Coastguard Worker                                                                const Expression& ifTrue,
341*c8dee2aaSAndroid Build Coastguard Worker                                                                const Expression& ifFalse);
342*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushVariableReference(const VariableReference& v);
343*c8dee2aaSAndroid Build Coastguard Worker 
344*c8dee2aaSAndroid Build Coastguard Worker     /** Support methods for immutable data, which trade more slots for smaller code size. */
345*c8dee2aaSAndroid Build Coastguard Worker     using ImmutableBits = int32_t;
346*c8dee2aaSAndroid Build Coastguard Worker 
347*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushImmutableData(const Expression& e);
348*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] std::optional<SlotRange> findPreexistingImmutableData(
349*c8dee2aaSAndroid Build Coastguard Worker             const TArray<ImmutableBits>& immutableValues);
350*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] std::optional<ImmutableBits> getImmutableBitsForSlot(const Expression& expr,
351*c8dee2aaSAndroid Build Coastguard Worker                                                                        size_t slot);
352*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool getImmutableValueForExpression(const Expression& expr,
353*c8dee2aaSAndroid Build Coastguard Worker                                                       TArray<ImmutableBits>* immutableValues);
354*c8dee2aaSAndroid Build Coastguard Worker     void storeImmutableValueToSlots(const TArray<ImmutableBits>& immutableValues, SlotRange slots);
355*c8dee2aaSAndroid Build Coastguard Worker 
356*c8dee2aaSAndroid Build Coastguard Worker     /** Pops an expression from the value stack and copies it into slots. */
popToSlotRange(SlotRange r)357*c8dee2aaSAndroid Build Coastguard Worker     void popToSlotRange(SlotRange r) {
358*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.pop_slots(r);
359*c8dee2aaSAndroid Build Coastguard Worker         if (this->shouldWriteTraceOps()) {
360*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.trace_var(fTraceMask->stackID(), r);
361*c8dee2aaSAndroid Build Coastguard Worker         }
362*c8dee2aaSAndroid Build Coastguard Worker     }
popToSlotRangeUnmasked(SlotRange r)363*c8dee2aaSAndroid Build Coastguard Worker     void popToSlotRangeUnmasked(SlotRange r) {
364*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.pop_slots_unmasked(r);
365*c8dee2aaSAndroid Build Coastguard Worker         if (this->shouldWriteTraceOps()) {
366*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.trace_var(fTraceMask->stackID(), r);
367*c8dee2aaSAndroid Build Coastguard Worker         }
368*c8dee2aaSAndroid Build Coastguard Worker     }
369*c8dee2aaSAndroid Build Coastguard Worker 
370*c8dee2aaSAndroid Build Coastguard Worker     /** Pops an expression from the value stack and discards it. */
discardExpression(int slots)371*c8dee2aaSAndroid Build Coastguard Worker     void discardExpression(int slots) { fBuilder.discard_stack(slots); }
372*c8dee2aaSAndroid Build Coastguard Worker 
373*c8dee2aaSAndroid Build Coastguard Worker     /** Zeroes out a range of slots. */
zeroSlotRangeUnmasked(SlotRange r)374*c8dee2aaSAndroid Build Coastguard Worker     void zeroSlotRangeUnmasked(SlotRange r) {
375*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.zero_slots_unmasked(r);
376*c8dee2aaSAndroid Build Coastguard Worker         if (this->shouldWriteTraceOps()) {
377*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.trace_var(fTraceMask->stackID(), r);
378*c8dee2aaSAndroid Build Coastguard Worker         }
379*c8dee2aaSAndroid Build Coastguard Worker     }
380*c8dee2aaSAndroid Build Coastguard Worker 
381*c8dee2aaSAndroid Build Coastguard Worker     /**
382*c8dee2aaSAndroid Build Coastguard Worker      * Emits a trace_line opcode. writeStatement does this, and statements that alter control flow
383*c8dee2aaSAndroid Build Coastguard Worker      * may need to explicitly add additional traces.
384*c8dee2aaSAndroid Build Coastguard Worker      */
385*c8dee2aaSAndroid Build Coastguard Worker     void emitTraceLine(Position pos);
386*c8dee2aaSAndroid Build Coastguard Worker 
387*c8dee2aaSAndroid Build Coastguard Worker     /**
388*c8dee2aaSAndroid Build Coastguard Worker      * Emits a trace_scope opcode, which alters the SkSL variable-scope depth.
389*c8dee2aaSAndroid Build Coastguard Worker      * Unlike the other trace ops, trace_scope takes a dedicated mask instead of the trace-scope
390*c8dee2aaSAndroid Build Coastguard Worker      * mask. Call `pushTraceScopeMask` to synthesize this mask; discard it when you're done.
391*c8dee2aaSAndroid Build Coastguard Worker      */
392*c8dee2aaSAndroid Build Coastguard Worker     void pushTraceScopeMask();
393*c8dee2aaSAndroid Build Coastguard Worker     void discardTraceScopeMask();
394*c8dee2aaSAndroid Build Coastguard Worker     void emitTraceScope(int delta);
395*c8dee2aaSAndroid Build Coastguard Worker 
396*c8dee2aaSAndroid Build Coastguard Worker     /** Prepares our position-to-line-offset conversion table (stored in `fLineOffsets`). */
397*c8dee2aaSAndroid Build Coastguard Worker     void calculateLineOffsets();
398*c8dee2aaSAndroid Build Coastguard Worker 
shouldWriteTraceOps()399*c8dee2aaSAndroid Build Coastguard Worker     bool shouldWriteTraceOps() { return fDebugTrace && fWriteTraceOps; }
traceMaskStackID()400*c8dee2aaSAndroid Build Coastguard Worker     int traceMaskStackID() { return fTraceMask->stackID(); }
401*c8dee2aaSAndroid Build Coastguard Worker 
402*c8dee2aaSAndroid Build Coastguard Worker     /** Expression utilities. */
403*c8dee2aaSAndroid Build Coastguard Worker     struct TypedOps {
404*c8dee2aaSAndroid Build Coastguard Worker         BuilderOp fFloatOp;
405*c8dee2aaSAndroid Build Coastguard Worker         BuilderOp fSignedOp;
406*c8dee2aaSAndroid Build Coastguard Worker         BuilderOp fUnsignedOp;
407*c8dee2aaSAndroid Build Coastguard Worker         BuilderOp fBooleanOp;
408*c8dee2aaSAndroid Build Coastguard Worker     };
409*c8dee2aaSAndroid Build Coastguard Worker 
410*c8dee2aaSAndroid Build Coastguard Worker     static BuilderOp GetTypedOp(const SkSL::Type& type, const TypedOps& ops);
411*c8dee2aaSAndroid Build Coastguard Worker 
412*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool unaryOp(const SkSL::Type& type, const TypedOps& ops);
413*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool binaryOp(const SkSL::Type& type, const TypedOps& ops);
414*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool ternaryOp(const SkSL::Type& type, const TypedOps& ops);
415*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushIntrinsic(const TypedOps& ops, const Expression& arg0);
416*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushIntrinsic(const TypedOps& ops,
417*c8dee2aaSAndroid Build Coastguard Worker                                      const Expression& arg0,
418*c8dee2aaSAndroid Build Coastguard Worker                                      const Expression& arg1);
419*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushIntrinsic(BuilderOp builderOp, const Expression& arg0);
420*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushIntrinsic(BuilderOp builderOp,
421*c8dee2aaSAndroid Build Coastguard Worker                                      const Expression& arg0,
422*c8dee2aaSAndroid Build Coastguard Worker                                      const Expression& arg1);
423*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushAbsFloatIntrinsic(int slots);
424*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushLengthIntrinsic(int slotCount);
425*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushVectorizedExpression(const Expression& expr, const Type& vectorType);
426*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushVariableReferencePartial(const VariableReference& v, SlotRange subset);
427*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushLValueOrExpression(LValue* lvalue, const Expression& expr);
428*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushMatrixMultiply(LValue* lvalue,
429*c8dee2aaSAndroid Build Coastguard Worker                                           const Expression& left,
430*c8dee2aaSAndroid Build Coastguard Worker                                           const Expression& right,
431*c8dee2aaSAndroid Build Coastguard Worker                                           int leftColumns, int leftRows,
432*c8dee2aaSAndroid Build Coastguard Worker                                           int rightColumns, int rightRows);
433*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool pushStructuredComparison(LValue* left,
434*c8dee2aaSAndroid Build Coastguard Worker                                                 Operator op,
435*c8dee2aaSAndroid Build Coastguard Worker                                                 LValue* right,
436*c8dee2aaSAndroid Build Coastguard Worker                                                 const Type& type);
437*c8dee2aaSAndroid Build Coastguard Worker 
438*c8dee2aaSAndroid Build Coastguard Worker     void foldWithMultiOp(BuilderOp op, int elements);
439*c8dee2aaSAndroid Build Coastguard Worker     void foldComparisonOp(Operator op, int elements);
440*c8dee2aaSAndroid Build Coastguard Worker 
441*c8dee2aaSAndroid Build Coastguard Worker     BuilderOp getTypedOp(const SkSL::Type& type, const TypedOps& ops) const;
442*c8dee2aaSAndroid Build Coastguard Worker 
returnComplexity(const FunctionDefinition * func)443*c8dee2aaSAndroid Build Coastguard Worker     Analysis::ReturnComplexity returnComplexity(const FunctionDefinition* func) {
444*c8dee2aaSAndroid Build Coastguard Worker         Analysis::ReturnComplexity* complexity = fReturnComplexityMap.find(func);
445*c8dee2aaSAndroid Build Coastguard Worker         if (!complexity) {
446*c8dee2aaSAndroid Build Coastguard Worker             complexity = fReturnComplexityMap.set(fCurrentFunction,
447*c8dee2aaSAndroid Build Coastguard Worker                                                   Analysis::GetReturnComplexity(*func));
448*c8dee2aaSAndroid Build Coastguard Worker         }
449*c8dee2aaSAndroid Build Coastguard Worker         return *complexity;
450*c8dee2aaSAndroid Build Coastguard Worker     }
451*c8dee2aaSAndroid Build Coastguard Worker 
needsReturnMask(const FunctionDefinition * func)452*c8dee2aaSAndroid Build Coastguard Worker     bool needsReturnMask(const FunctionDefinition* func) {
453*c8dee2aaSAndroid Build Coastguard Worker         return this->returnComplexity(func) >= Analysis::ReturnComplexity::kEarlyReturns;
454*c8dee2aaSAndroid Build Coastguard Worker     }
455*c8dee2aaSAndroid Build Coastguard Worker 
needsFunctionResultSlots(const FunctionDefinition * func)456*c8dee2aaSAndroid Build Coastguard Worker     bool needsFunctionResultSlots(const FunctionDefinition* func) {
457*c8dee2aaSAndroid Build Coastguard Worker         return this->shouldWriteTraceOps() || (this->returnComplexity(func) >
458*c8dee2aaSAndroid Build Coastguard Worker                                                Analysis::ReturnComplexity::kSingleSafeReturn);
459*c8dee2aaSAndroid Build Coastguard Worker     }
460*c8dee2aaSAndroid Build Coastguard Worker 
IsUniform(const Variable & var)461*c8dee2aaSAndroid Build Coastguard Worker     static bool IsUniform(const Variable& var) {
462*c8dee2aaSAndroid Build Coastguard Worker        return var.modifierFlags().isUniform();
463*c8dee2aaSAndroid Build Coastguard Worker     }
464*c8dee2aaSAndroid Build Coastguard Worker 
IsOutParameter(const Variable & var)465*c8dee2aaSAndroid Build Coastguard Worker     static bool IsOutParameter(const Variable& var) {
466*c8dee2aaSAndroid Build Coastguard Worker         return (var.modifierFlags() & (ModifierFlag::kIn | ModifierFlag::kOut)) ==
467*c8dee2aaSAndroid Build Coastguard Worker                ModifierFlag::kOut;
468*c8dee2aaSAndroid Build Coastguard Worker     }
469*c8dee2aaSAndroid Build Coastguard Worker 
IsInoutParameter(const Variable & var)470*c8dee2aaSAndroid Build Coastguard Worker     static bool IsInoutParameter(const Variable& var) {
471*c8dee2aaSAndroid Build Coastguard Worker         return (var.modifierFlags() & (ModifierFlag::kIn | ModifierFlag::kOut)) ==
472*c8dee2aaSAndroid Build Coastguard Worker                (ModifierFlag::kIn | ModifierFlag::kOut);
473*c8dee2aaSAndroid Build Coastguard Worker     }
474*c8dee2aaSAndroid Build Coastguard Worker 
475*c8dee2aaSAndroid Build Coastguard Worker private:
476*c8dee2aaSAndroid Build Coastguard Worker     const SkSL::Program& fProgram;
477*c8dee2aaSAndroid Build Coastguard Worker     SkSL::Context fContext;
478*c8dee2aaSAndroid Build Coastguard Worker     Builder fBuilder;
479*c8dee2aaSAndroid Build Coastguard Worker     DebugTracePriv* fDebugTrace = nullptr;
480*c8dee2aaSAndroid Build Coastguard Worker     bool fWriteTraceOps = false;
481*c8dee2aaSAndroid Build Coastguard Worker     THashMap<const Variable*, int> fChildEffectMap;
482*c8dee2aaSAndroid Build Coastguard Worker 
483*c8dee2aaSAndroid Build Coastguard Worker     SlotManager fProgramSlots;
484*c8dee2aaSAndroid Build Coastguard Worker     SlotManager fUniformSlots;
485*c8dee2aaSAndroid Build Coastguard Worker     SlotManager fImmutableSlots;
486*c8dee2aaSAndroid Build Coastguard Worker 
487*c8dee2aaSAndroid Build Coastguard Worker     std::optional<AutoStack> fTraceMask;
488*c8dee2aaSAndroid Build Coastguard Worker     const FunctionDefinition* fCurrentFunction = nullptr;
489*c8dee2aaSAndroid Build Coastguard Worker     SlotRange fCurrentFunctionResult;
490*c8dee2aaSAndroid Build Coastguard Worker     AutoContinueMask* fCurrentContinueMask = nullptr;
491*c8dee2aaSAndroid Build Coastguard Worker     int fCurrentBreakTarget = -1;
492*c8dee2aaSAndroid Build Coastguard Worker     int fCurrentStack = 0;
493*c8dee2aaSAndroid Build Coastguard Worker     int fNextStackID = 0;
494*c8dee2aaSAndroid Build Coastguard Worker     TArray<int> fRecycledStacks;
495*c8dee2aaSAndroid Build Coastguard Worker 
496*c8dee2aaSAndroid Build Coastguard Worker     THashMap<const FunctionDefinition*, Analysis::ReturnComplexity> fReturnComplexityMap;
497*c8dee2aaSAndroid Build Coastguard Worker 
498*c8dee2aaSAndroid Build Coastguard Worker     THashMap<ImmutableBits, THashSet<Slot>> fImmutableSlotMap;
499*c8dee2aaSAndroid Build Coastguard Worker     THashSet<const Variable*> fImmutableVariables;
500*c8dee2aaSAndroid Build Coastguard Worker 
501*c8dee2aaSAndroid Build Coastguard Worker     // `fInsideCompoundStatement` will be nonzero if we are currently writing statements inside of a
502*c8dee2aaSAndroid Build Coastguard Worker     // compound-statement Block. (Conceptually those statements should all count as one.)
503*c8dee2aaSAndroid Build Coastguard Worker     int fInsideCompoundStatement = 0;
504*c8dee2aaSAndroid Build Coastguard Worker 
505*c8dee2aaSAndroid Build Coastguard Worker     // `fLineOffsets` contains the position of each newline in the source, plus a zero at the
506*c8dee2aaSAndroid Build Coastguard Worker     // beginning, and the total source length at the end, as sentinels.
507*c8dee2aaSAndroid Build Coastguard Worker     TArray<int> fLineOffsets;
508*c8dee2aaSAndroid Build Coastguard Worker 
509*c8dee2aaSAndroid Build Coastguard Worker     static constexpr auto kAddOps = TypedOps{BuilderOp::add_n_floats,
510*c8dee2aaSAndroid Build Coastguard Worker                                              BuilderOp::add_n_ints,
511*c8dee2aaSAndroid Build Coastguard Worker                                              BuilderOp::add_n_ints,
512*c8dee2aaSAndroid Build Coastguard Worker                                              BuilderOp::unsupported};
513*c8dee2aaSAndroid Build Coastguard Worker     static constexpr auto kSubtractOps = TypedOps{BuilderOp::sub_n_floats,
514*c8dee2aaSAndroid Build Coastguard Worker                                                   BuilderOp::sub_n_ints,
515*c8dee2aaSAndroid Build Coastguard Worker                                                   BuilderOp::sub_n_ints,
516*c8dee2aaSAndroid Build Coastguard Worker                                                   BuilderOp::unsupported};
517*c8dee2aaSAndroid Build Coastguard Worker     static constexpr auto kMultiplyOps = TypedOps{BuilderOp::mul_n_floats,
518*c8dee2aaSAndroid Build Coastguard Worker                                                   BuilderOp::mul_n_ints,
519*c8dee2aaSAndroid Build Coastguard Worker                                                   BuilderOp::mul_n_ints,
520*c8dee2aaSAndroid Build Coastguard Worker                                                   BuilderOp::unsupported};
521*c8dee2aaSAndroid Build Coastguard Worker     static constexpr auto kDivideOps = TypedOps{BuilderOp::div_n_floats,
522*c8dee2aaSAndroid Build Coastguard Worker                                                 BuilderOp::div_n_ints,
523*c8dee2aaSAndroid Build Coastguard Worker                                                 BuilderOp::div_n_uints,
524*c8dee2aaSAndroid Build Coastguard Worker                                                 BuilderOp::unsupported};
525*c8dee2aaSAndroid Build Coastguard Worker     static constexpr auto kLessThanOps = TypedOps{BuilderOp::cmplt_n_floats,
526*c8dee2aaSAndroid Build Coastguard Worker                                                   BuilderOp::cmplt_n_ints,
527*c8dee2aaSAndroid Build Coastguard Worker                                                   BuilderOp::cmplt_n_uints,
528*c8dee2aaSAndroid Build Coastguard Worker                                                   BuilderOp::unsupported};
529*c8dee2aaSAndroid Build Coastguard Worker     static constexpr auto kLessThanEqualOps = TypedOps{BuilderOp::cmple_n_floats,
530*c8dee2aaSAndroid Build Coastguard Worker                                                        BuilderOp::cmple_n_ints,
531*c8dee2aaSAndroid Build Coastguard Worker                                                        BuilderOp::cmple_n_uints,
532*c8dee2aaSAndroid Build Coastguard Worker                                                        BuilderOp::unsupported};
533*c8dee2aaSAndroid Build Coastguard Worker     static constexpr auto kEqualOps = TypedOps{BuilderOp::cmpeq_n_floats,
534*c8dee2aaSAndroid Build Coastguard Worker                                                BuilderOp::cmpeq_n_ints,
535*c8dee2aaSAndroid Build Coastguard Worker                                                BuilderOp::cmpeq_n_ints,
536*c8dee2aaSAndroid Build Coastguard Worker                                                BuilderOp::cmpeq_n_ints};
537*c8dee2aaSAndroid Build Coastguard Worker     static constexpr auto kNotEqualOps = TypedOps{BuilderOp::cmpne_n_floats,
538*c8dee2aaSAndroid Build Coastguard Worker                                                   BuilderOp::cmpne_n_ints,
539*c8dee2aaSAndroid Build Coastguard Worker                                                   BuilderOp::cmpne_n_ints,
540*c8dee2aaSAndroid Build Coastguard Worker                                                   BuilderOp::cmpne_n_ints};
541*c8dee2aaSAndroid Build Coastguard Worker     static constexpr auto kModOps = TypedOps{BuilderOp::mod_n_floats,
542*c8dee2aaSAndroid Build Coastguard Worker                                              BuilderOp::unsupported,
543*c8dee2aaSAndroid Build Coastguard Worker                                              BuilderOp::unsupported,
544*c8dee2aaSAndroid Build Coastguard Worker                                              BuilderOp::unsupported};
545*c8dee2aaSAndroid Build Coastguard Worker     static constexpr auto kMinOps = TypedOps{BuilderOp::min_n_floats,
546*c8dee2aaSAndroid Build Coastguard Worker                                              BuilderOp::min_n_ints,
547*c8dee2aaSAndroid Build Coastguard Worker                                              BuilderOp::min_n_uints,
548*c8dee2aaSAndroid Build Coastguard Worker                                              BuilderOp::min_n_uints};
549*c8dee2aaSAndroid Build Coastguard Worker     static constexpr auto kMaxOps = TypedOps{BuilderOp::max_n_floats,
550*c8dee2aaSAndroid Build Coastguard Worker                                              BuilderOp::max_n_ints,
551*c8dee2aaSAndroid Build Coastguard Worker                                              BuilderOp::max_n_uints,
552*c8dee2aaSAndroid Build Coastguard Worker                                              BuilderOp::max_n_uints};
553*c8dee2aaSAndroid Build Coastguard Worker     static constexpr auto kMixOps = TypedOps{BuilderOp::mix_n_floats,
554*c8dee2aaSAndroid Build Coastguard Worker                                              BuilderOp::unsupported,
555*c8dee2aaSAndroid Build Coastguard Worker                                              BuilderOp::unsupported,
556*c8dee2aaSAndroid Build Coastguard Worker                                              BuilderOp::unsupported};
557*c8dee2aaSAndroid Build Coastguard Worker     static constexpr auto kInverseSqrtOps = TypedOps{BuilderOp::invsqrt_float,
558*c8dee2aaSAndroid Build Coastguard Worker                                                      BuilderOp::unsupported,
559*c8dee2aaSAndroid Build Coastguard Worker                                                      BuilderOp::unsupported,
560*c8dee2aaSAndroid Build Coastguard Worker                                                      BuilderOp::unsupported};
561*c8dee2aaSAndroid Build Coastguard Worker     friend class AutoContinueMask;
562*c8dee2aaSAndroid Build Coastguard Worker };
563*c8dee2aaSAndroid Build Coastguard Worker 
AutoStack(Generator * g)564*c8dee2aaSAndroid Build Coastguard Worker AutoStack::AutoStack(Generator* g)
565*c8dee2aaSAndroid Build Coastguard Worker         : fGenerator(g)
566*c8dee2aaSAndroid Build Coastguard Worker         , fStackID(g->createStack()) {}
567*c8dee2aaSAndroid Build Coastguard Worker 
~AutoStack()568*c8dee2aaSAndroid Build Coastguard Worker AutoStack::~AutoStack() {
569*c8dee2aaSAndroid Build Coastguard Worker     fGenerator->recycleStack(fStackID);
570*c8dee2aaSAndroid Build Coastguard Worker }
571*c8dee2aaSAndroid Build Coastguard Worker 
enter()572*c8dee2aaSAndroid Build Coastguard Worker void AutoStack::enter() {
573*c8dee2aaSAndroid Build Coastguard Worker     fParentStackID = fGenerator->currentStack();
574*c8dee2aaSAndroid Build Coastguard Worker     fGenerator->setCurrentStack(fStackID);
575*c8dee2aaSAndroid Build Coastguard Worker }
576*c8dee2aaSAndroid Build Coastguard Worker 
exit()577*c8dee2aaSAndroid Build Coastguard Worker void AutoStack::exit() {
578*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fGenerator->currentStack() == fStackID);
579*c8dee2aaSAndroid Build Coastguard Worker     fGenerator->setCurrentStack(fParentStackID);
580*c8dee2aaSAndroid Build Coastguard Worker }
581*c8dee2aaSAndroid Build Coastguard Worker 
pushClone(int slots)582*c8dee2aaSAndroid Build Coastguard Worker void AutoStack::pushClone(int slots) {
583*c8dee2aaSAndroid Build Coastguard Worker     this->pushClone(SlotRange{0, slots}, /*offsetFromStackTop=*/slots);
584*c8dee2aaSAndroid Build Coastguard Worker }
585*c8dee2aaSAndroid Build Coastguard Worker 
pushClone(SlotRange range,int offsetFromStackTop)586*c8dee2aaSAndroid Build Coastguard Worker void AutoStack::pushClone(SlotRange range, int offsetFromStackTop) {
587*c8dee2aaSAndroid Build Coastguard Worker     fGenerator->builder()->push_clone_from_stack(range, fStackID, offsetFromStackTop);
588*c8dee2aaSAndroid Build Coastguard Worker }
589*c8dee2aaSAndroid Build Coastguard Worker 
pushCloneIndirect(SlotRange range,int dynamicStackID,int offsetFromStackTop)590*c8dee2aaSAndroid Build Coastguard Worker void AutoStack::pushCloneIndirect(SlotRange range, int dynamicStackID, int offsetFromStackTop) {
591*c8dee2aaSAndroid Build Coastguard Worker     fGenerator->builder()->push_clone_indirect_from_stack(
592*c8dee2aaSAndroid Build Coastguard Worker             range, dynamicStackID, /*otherStackID=*/fStackID, offsetFromStackTop);
593*c8dee2aaSAndroid Build Coastguard Worker }
594*c8dee2aaSAndroid Build Coastguard Worker 
595*c8dee2aaSAndroid Build Coastguard Worker class AutoContinueMask {
596*c8dee2aaSAndroid Build Coastguard Worker public:
AutoContinueMask(Generator * gen)597*c8dee2aaSAndroid Build Coastguard Worker     AutoContinueMask(Generator* gen) : fGenerator(gen) {}
598*c8dee2aaSAndroid Build Coastguard Worker 
~AutoContinueMask()599*c8dee2aaSAndroid Build Coastguard Worker     ~AutoContinueMask() {
600*c8dee2aaSAndroid Build Coastguard Worker         if (fPreviousContinueMask) {
601*c8dee2aaSAndroid Build Coastguard Worker             fGenerator->fCurrentContinueMask = fPreviousContinueMask;
602*c8dee2aaSAndroid Build Coastguard Worker         }
603*c8dee2aaSAndroid Build Coastguard Worker     }
604*c8dee2aaSAndroid Build Coastguard Worker 
enable()605*c8dee2aaSAndroid Build Coastguard Worker     void enable() {
606*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!fContinueMaskStack.has_value());
607*c8dee2aaSAndroid Build Coastguard Worker 
608*c8dee2aaSAndroid Build Coastguard Worker         fContinueMaskStack.emplace(fGenerator);
609*c8dee2aaSAndroid Build Coastguard Worker         fPreviousContinueMask = fGenerator->fCurrentContinueMask;
610*c8dee2aaSAndroid Build Coastguard Worker         fGenerator->fCurrentContinueMask = this;
611*c8dee2aaSAndroid Build Coastguard Worker     }
612*c8dee2aaSAndroid Build Coastguard Worker 
enter()613*c8dee2aaSAndroid Build Coastguard Worker     void enter() {
614*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fContinueMaskStack.has_value());
615*c8dee2aaSAndroid Build Coastguard Worker         fContinueMaskStack->enter();
616*c8dee2aaSAndroid Build Coastguard Worker     }
617*c8dee2aaSAndroid Build Coastguard Worker 
exit()618*c8dee2aaSAndroid Build Coastguard Worker     void exit() {
619*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fContinueMaskStack.has_value());
620*c8dee2aaSAndroid Build Coastguard Worker         fContinueMaskStack->exit();
621*c8dee2aaSAndroid Build Coastguard Worker     }
622*c8dee2aaSAndroid Build Coastguard Worker 
enterLoopBody()623*c8dee2aaSAndroid Build Coastguard Worker     void enterLoopBody() {
624*c8dee2aaSAndroid Build Coastguard Worker         if (fContinueMaskStack.has_value()) {
625*c8dee2aaSAndroid Build Coastguard Worker             fContinueMaskStack->enter();
626*c8dee2aaSAndroid Build Coastguard Worker             fGenerator->builder()->push_constant_i(0);
627*c8dee2aaSAndroid Build Coastguard Worker             fContinueMaskStack->exit();
628*c8dee2aaSAndroid Build Coastguard Worker         }
629*c8dee2aaSAndroid Build Coastguard Worker     }
630*c8dee2aaSAndroid Build Coastguard Worker 
exitLoopBody()631*c8dee2aaSAndroid Build Coastguard Worker     void exitLoopBody() {
632*c8dee2aaSAndroid Build Coastguard Worker         if (fContinueMaskStack.has_value()) {
633*c8dee2aaSAndroid Build Coastguard Worker             fContinueMaskStack->enter();
634*c8dee2aaSAndroid Build Coastguard Worker             fGenerator->builder()->pop_and_reenable_loop_mask();
635*c8dee2aaSAndroid Build Coastguard Worker             fContinueMaskStack->exit();
636*c8dee2aaSAndroid Build Coastguard Worker         }
637*c8dee2aaSAndroid Build Coastguard Worker     }
638*c8dee2aaSAndroid Build Coastguard Worker 
stackID()639*c8dee2aaSAndroid Build Coastguard Worker     int stackID() {
640*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fContinueMaskStack.has_value());
641*c8dee2aaSAndroid Build Coastguard Worker         return fContinueMaskStack->stackID();
642*c8dee2aaSAndroid Build Coastguard Worker     }
643*c8dee2aaSAndroid Build Coastguard Worker 
644*c8dee2aaSAndroid Build Coastguard Worker private:
645*c8dee2aaSAndroid Build Coastguard Worker     std::optional<AutoStack> fContinueMaskStack;
646*c8dee2aaSAndroid Build Coastguard Worker     Generator* fGenerator = nullptr;
647*c8dee2aaSAndroid Build Coastguard Worker     AutoContinueMask* fPreviousContinueMask = nullptr;
648*c8dee2aaSAndroid Build Coastguard Worker };
649*c8dee2aaSAndroid Build Coastguard Worker 
650*c8dee2aaSAndroid Build Coastguard Worker class AutoLoopTarget {
651*c8dee2aaSAndroid Build Coastguard Worker public:
AutoLoopTarget(Generator * gen,int * targetPtr)652*c8dee2aaSAndroid Build Coastguard Worker     AutoLoopTarget(Generator* gen, int* targetPtr) : fGenerator(gen), fLoopTargetPtr(targetPtr) {
653*c8dee2aaSAndroid Build Coastguard Worker         fLabelID = fGenerator->builder()->nextLabelID();
654*c8dee2aaSAndroid Build Coastguard Worker         fPreviousLoopTarget = *fLoopTargetPtr;
655*c8dee2aaSAndroid Build Coastguard Worker         *fLoopTargetPtr = fLabelID;
656*c8dee2aaSAndroid Build Coastguard Worker     }
657*c8dee2aaSAndroid Build Coastguard Worker 
~AutoLoopTarget()658*c8dee2aaSAndroid Build Coastguard Worker     ~AutoLoopTarget() {
659*c8dee2aaSAndroid Build Coastguard Worker         *fLoopTargetPtr = fPreviousLoopTarget;
660*c8dee2aaSAndroid Build Coastguard Worker     }
661*c8dee2aaSAndroid Build Coastguard Worker 
labelID()662*c8dee2aaSAndroid Build Coastguard Worker     int labelID() {
663*c8dee2aaSAndroid Build Coastguard Worker         return fLabelID;
664*c8dee2aaSAndroid Build Coastguard Worker     }
665*c8dee2aaSAndroid Build Coastguard Worker 
666*c8dee2aaSAndroid Build Coastguard Worker private:
667*c8dee2aaSAndroid Build Coastguard Worker     Generator* fGenerator = nullptr;
668*c8dee2aaSAndroid Build Coastguard Worker     int* fLoopTargetPtr = nullptr;
669*c8dee2aaSAndroid Build Coastguard Worker     int fPreviousLoopTarget;
670*c8dee2aaSAndroid Build Coastguard Worker     int fLabelID;
671*c8dee2aaSAndroid Build Coastguard Worker };
672*c8dee2aaSAndroid Build Coastguard Worker 
673*c8dee2aaSAndroid Build Coastguard Worker class LValue {
674*c8dee2aaSAndroid Build Coastguard Worker public:
675*c8dee2aaSAndroid Build Coastguard Worker     virtual ~LValue() = default;
676*c8dee2aaSAndroid Build Coastguard Worker 
677*c8dee2aaSAndroid Build Coastguard Worker     /** Returns true if this lvalue is actually writable--temporaries and uniforms are not. */
678*c8dee2aaSAndroid Build Coastguard Worker     virtual bool isWritable() const = 0;
679*c8dee2aaSAndroid Build Coastguard Worker 
680*c8dee2aaSAndroid Build Coastguard Worker     /**
681*c8dee2aaSAndroid Build Coastguard Worker      * Returns the fixed slot range of the lvalue, after it is winnowed down to the selected
682*c8dee2aaSAndroid Build Coastguard Worker      * field/index. The range is calculated assuming every dynamic index will evaluate to zero.
683*c8dee2aaSAndroid Build Coastguard Worker      */
684*c8dee2aaSAndroid Build Coastguard Worker     virtual SlotRange fixedSlotRange(Generator* gen) = 0;
685*c8dee2aaSAndroid Build Coastguard Worker 
686*c8dee2aaSAndroid Build Coastguard Worker     /**
687*c8dee2aaSAndroid Build Coastguard Worker      * Returns a stack which holds a single integer, representing the dynamic offset of the lvalue.
688*c8dee2aaSAndroid Build Coastguard Worker      * This value does not incorporate the fixed offset. If null is returned, the lvalue doesn't
689*c8dee2aaSAndroid Build Coastguard Worker      * have a dynamic offset. `evaluateDynamicIndices` must be called before this is used.
690*c8dee2aaSAndroid Build Coastguard Worker      */
691*c8dee2aaSAndroid Build Coastguard Worker     virtual AutoStack* dynamicSlotRange() = 0;
692*c8dee2aaSAndroid Build Coastguard Worker 
693*c8dee2aaSAndroid Build Coastguard Worker     /** Returns the swizzle components of the lvalue, or an empty span for non-swizzle LValues. */
swizzle()694*c8dee2aaSAndroid Build Coastguard Worker     virtual SkSpan<const int8_t> swizzle() { return {}; }
695*c8dee2aaSAndroid Build Coastguard Worker 
696*c8dee2aaSAndroid Build Coastguard Worker     /** Pushes values directly onto the stack. */
697*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] virtual bool push(Generator* gen,
698*c8dee2aaSAndroid Build Coastguard Worker                                     SlotRange fixedOffset,
699*c8dee2aaSAndroid Build Coastguard Worker                                     AutoStack* dynamicOffset,
700*c8dee2aaSAndroid Build Coastguard Worker                                     SkSpan<const int8_t> swizzle) = 0;
701*c8dee2aaSAndroid Build Coastguard Worker 
702*c8dee2aaSAndroid Build Coastguard Worker     /** Stores topmost values from the stack directly into the lvalue. */
703*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] virtual bool store(Generator* gen,
704*c8dee2aaSAndroid Build Coastguard Worker                                      SlotRange fixedOffset,
705*c8dee2aaSAndroid Build Coastguard Worker                                      AutoStack* dynamicOffset,
706*c8dee2aaSAndroid Build Coastguard Worker                                      SkSpan<const int8_t> swizzle) = 0;
707*c8dee2aaSAndroid Build Coastguard Worker     /**
708*c8dee2aaSAndroid Build Coastguard Worker      * Some lvalues refer to a temporary expression; these temps can be held in the
709*c8dee2aaSAndroid Build Coastguard Worker      * scratch-expression field to ensure that they exist for the lifetime of the lvalue.
710*c8dee2aaSAndroid Build Coastguard Worker      */
711*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Expression> fScratchExpression;
712*c8dee2aaSAndroid Build Coastguard Worker };
713*c8dee2aaSAndroid Build Coastguard Worker 
714*c8dee2aaSAndroid Build Coastguard Worker class ScratchLValue final : public LValue {
715*c8dee2aaSAndroid Build Coastguard Worker public:
ScratchLValue(const Expression & e)716*c8dee2aaSAndroid Build Coastguard Worker     explicit ScratchLValue(const Expression& e)
717*c8dee2aaSAndroid Build Coastguard Worker             : fExpression(&e)
718*c8dee2aaSAndroid Build Coastguard Worker             , fNumSlots(e.type().slotCount()) {}
719*c8dee2aaSAndroid Build Coastguard Worker 
~ScratchLValue()720*c8dee2aaSAndroid Build Coastguard Worker     ~ScratchLValue() override {
721*c8dee2aaSAndroid Build Coastguard Worker         if (fGenerator && fDedicatedStack.has_value()) {
722*c8dee2aaSAndroid Build Coastguard Worker             // Jettison the scratch expression.
723*c8dee2aaSAndroid Build Coastguard Worker             fDedicatedStack->enter();
724*c8dee2aaSAndroid Build Coastguard Worker             fGenerator->discardExpression(fNumSlots);
725*c8dee2aaSAndroid Build Coastguard Worker             fDedicatedStack->exit();
726*c8dee2aaSAndroid Build Coastguard Worker         }
727*c8dee2aaSAndroid Build Coastguard Worker     }
728*c8dee2aaSAndroid Build Coastguard Worker 
isWritable() const729*c8dee2aaSAndroid Build Coastguard Worker     bool isWritable() const override {
730*c8dee2aaSAndroid Build Coastguard Worker         return false;
731*c8dee2aaSAndroid Build Coastguard Worker     }
732*c8dee2aaSAndroid Build Coastguard Worker 
fixedSlotRange(Generator * gen)733*c8dee2aaSAndroid Build Coastguard Worker     SlotRange fixedSlotRange(Generator* gen) override {
734*c8dee2aaSAndroid Build Coastguard Worker         return SlotRange{0, fNumSlots};
735*c8dee2aaSAndroid Build Coastguard Worker     }
736*c8dee2aaSAndroid Build Coastguard Worker 
dynamicSlotRange()737*c8dee2aaSAndroid Build Coastguard Worker     AutoStack* dynamicSlotRange() override {
738*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
739*c8dee2aaSAndroid Build Coastguard Worker     }
740*c8dee2aaSAndroid Build Coastguard Worker 
push(Generator * gen,SlotRange fixedOffset,AutoStack * dynamicOffset,SkSpan<const int8_t> swizzle)741*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool push(Generator* gen,
742*c8dee2aaSAndroid Build Coastguard Worker                             SlotRange fixedOffset,
743*c8dee2aaSAndroid Build Coastguard Worker                             AutoStack* dynamicOffset,
744*c8dee2aaSAndroid Build Coastguard Worker                             SkSpan<const int8_t> swizzle) override {
745*c8dee2aaSAndroid Build Coastguard Worker         if (!fDedicatedStack.has_value()) {
746*c8dee2aaSAndroid Build Coastguard Worker             // Push the scratch expression onto a dedicated stack.
747*c8dee2aaSAndroid Build Coastguard Worker             fGenerator = gen;
748*c8dee2aaSAndroid Build Coastguard Worker             fDedicatedStack.emplace(fGenerator);
749*c8dee2aaSAndroid Build Coastguard Worker             fDedicatedStack->enter();
750*c8dee2aaSAndroid Build Coastguard Worker             if (!fGenerator->pushExpression(*fExpression)) {
751*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
752*c8dee2aaSAndroid Build Coastguard Worker             }
753*c8dee2aaSAndroid Build Coastguard Worker             fDedicatedStack->exit();
754*c8dee2aaSAndroid Build Coastguard Worker         }
755*c8dee2aaSAndroid Build Coastguard Worker 
756*c8dee2aaSAndroid Build Coastguard Worker         if (dynamicOffset) {
757*c8dee2aaSAndroid Build Coastguard Worker             fDedicatedStack->pushCloneIndirect(fixedOffset, dynamicOffset->stackID(), fNumSlots);
758*c8dee2aaSAndroid Build Coastguard Worker         } else {
759*c8dee2aaSAndroid Build Coastguard Worker             fDedicatedStack->pushClone(fixedOffset, fNumSlots);
760*c8dee2aaSAndroid Build Coastguard Worker         }
761*c8dee2aaSAndroid Build Coastguard Worker         if (!swizzle.empty()) {
762*c8dee2aaSAndroid Build Coastguard Worker             gen->builder()->swizzle(fixedOffset.count, swizzle);
763*c8dee2aaSAndroid Build Coastguard Worker         }
764*c8dee2aaSAndroid Build Coastguard Worker         return true;
765*c8dee2aaSAndroid Build Coastguard Worker     }
766*c8dee2aaSAndroid Build Coastguard Worker 
store(Generator *,SlotRange,AutoStack *,SkSpan<const int8_t>)767*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool store(Generator*, SlotRange, AutoStack*, SkSpan<const int8_t>) override {
768*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGFAIL("scratch lvalues cannot be stored into");
769*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
770*c8dee2aaSAndroid Build Coastguard Worker     }
771*c8dee2aaSAndroid Build Coastguard Worker 
772*c8dee2aaSAndroid Build Coastguard Worker private:
773*c8dee2aaSAndroid Build Coastguard Worker     Generator* fGenerator = nullptr;
774*c8dee2aaSAndroid Build Coastguard Worker     const Expression* fExpression = nullptr;
775*c8dee2aaSAndroid Build Coastguard Worker     std::optional<AutoStack> fDedicatedStack;
776*c8dee2aaSAndroid Build Coastguard Worker     int fNumSlots = 0;
777*c8dee2aaSAndroid Build Coastguard Worker };
778*c8dee2aaSAndroid Build Coastguard Worker 
779*c8dee2aaSAndroid Build Coastguard Worker class VariableLValue final : public LValue {
780*c8dee2aaSAndroid Build Coastguard Worker public:
VariableLValue(const Variable * v)781*c8dee2aaSAndroid Build Coastguard Worker     explicit VariableLValue(const Variable* v) : fVariable(v) {}
782*c8dee2aaSAndroid Build Coastguard Worker 
isWritable() const783*c8dee2aaSAndroid Build Coastguard Worker     bool isWritable() const override {
784*c8dee2aaSAndroid Build Coastguard Worker         return !Generator::IsUniform(*fVariable);
785*c8dee2aaSAndroid Build Coastguard Worker     }
786*c8dee2aaSAndroid Build Coastguard Worker 
fixedSlotRange(Generator * gen)787*c8dee2aaSAndroid Build Coastguard Worker     SlotRange fixedSlotRange(Generator* gen) override {
788*c8dee2aaSAndroid Build Coastguard Worker         return Generator::IsUniform(*fVariable) ? gen->getUniformSlots(*fVariable)
789*c8dee2aaSAndroid Build Coastguard Worker                                                 : gen->getVariableSlots(*fVariable);
790*c8dee2aaSAndroid Build Coastguard Worker     }
791*c8dee2aaSAndroid Build Coastguard Worker 
dynamicSlotRange()792*c8dee2aaSAndroid Build Coastguard Worker     AutoStack* dynamicSlotRange() override {
793*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
794*c8dee2aaSAndroid Build Coastguard Worker     }
795*c8dee2aaSAndroid Build Coastguard Worker 
push(Generator * gen,SlotRange fixedOffset,AutoStack * dynamicOffset,SkSpan<const int8_t> swizzle)796*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool push(Generator* gen,
797*c8dee2aaSAndroid Build Coastguard Worker                             SlotRange fixedOffset,
798*c8dee2aaSAndroid Build Coastguard Worker                             AutoStack* dynamicOffset,
799*c8dee2aaSAndroid Build Coastguard Worker                             SkSpan<const int8_t> swizzle) override {
800*c8dee2aaSAndroid Build Coastguard Worker         if (Generator::IsUniform(*fVariable)) {
801*c8dee2aaSAndroid Build Coastguard Worker             if (dynamicOffset) {
802*c8dee2aaSAndroid Build Coastguard Worker                 gen->builder()->push_uniform_indirect(fixedOffset, dynamicOffset->stackID(),
803*c8dee2aaSAndroid Build Coastguard Worker                                                       this->fixedSlotRange(gen));
804*c8dee2aaSAndroid Build Coastguard Worker             } else {
805*c8dee2aaSAndroid Build Coastguard Worker                 gen->builder()->push_uniform(fixedOffset);
806*c8dee2aaSAndroid Build Coastguard Worker             }
807*c8dee2aaSAndroid Build Coastguard Worker         } else {
808*c8dee2aaSAndroid Build Coastguard Worker             if (dynamicOffset) {
809*c8dee2aaSAndroid Build Coastguard Worker                 gen->builder()->push_slots_indirect(fixedOffset, dynamicOffset->stackID(),
810*c8dee2aaSAndroid Build Coastguard Worker                                                     this->fixedSlotRange(gen));
811*c8dee2aaSAndroid Build Coastguard Worker             } else {
812*c8dee2aaSAndroid Build Coastguard Worker                 gen->builder()->push_slots(fixedOffset);
813*c8dee2aaSAndroid Build Coastguard Worker             }
814*c8dee2aaSAndroid Build Coastguard Worker         }
815*c8dee2aaSAndroid Build Coastguard Worker         if (!swizzle.empty()) {
816*c8dee2aaSAndroid Build Coastguard Worker             gen->builder()->swizzle(fixedOffset.count, swizzle);
817*c8dee2aaSAndroid Build Coastguard Worker         }
818*c8dee2aaSAndroid Build Coastguard Worker         return true;
819*c8dee2aaSAndroid Build Coastguard Worker     }
820*c8dee2aaSAndroid Build Coastguard Worker 
store(Generator * gen,SlotRange fixedOffset,AutoStack * dynamicOffset,SkSpan<const int8_t> swizzle)821*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool store(Generator* gen,
822*c8dee2aaSAndroid Build Coastguard Worker                              SlotRange fixedOffset,
823*c8dee2aaSAndroid Build Coastguard Worker                              AutoStack* dynamicOffset,
824*c8dee2aaSAndroid Build Coastguard Worker                              SkSpan<const int8_t> swizzle) override {
825*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!Generator::IsUniform(*fVariable));
826*c8dee2aaSAndroid Build Coastguard Worker 
827*c8dee2aaSAndroid Build Coastguard Worker         if (swizzle.empty()) {
828*c8dee2aaSAndroid Build Coastguard Worker             if (dynamicOffset) {
829*c8dee2aaSAndroid Build Coastguard Worker                 gen->builder()->copy_stack_to_slots_indirect(fixedOffset, dynamicOffset->stackID(),
830*c8dee2aaSAndroid Build Coastguard Worker                                                              this->fixedSlotRange(gen));
831*c8dee2aaSAndroid Build Coastguard Worker             } else {
832*c8dee2aaSAndroid Build Coastguard Worker                 gen->builder()->copy_stack_to_slots(fixedOffset);
833*c8dee2aaSAndroid Build Coastguard Worker             }
834*c8dee2aaSAndroid Build Coastguard Worker         } else {
835*c8dee2aaSAndroid Build Coastguard Worker             if (dynamicOffset) {
836*c8dee2aaSAndroid Build Coastguard Worker                 gen->builder()->swizzle_copy_stack_to_slots_indirect(fixedOffset,
837*c8dee2aaSAndroid Build Coastguard Worker                                                                      dynamicOffset->stackID(),
838*c8dee2aaSAndroid Build Coastguard Worker                                                                      this->fixedSlotRange(gen),
839*c8dee2aaSAndroid Build Coastguard Worker                                                                      swizzle,
840*c8dee2aaSAndroid Build Coastguard Worker                                                                      swizzle.size());
841*c8dee2aaSAndroid Build Coastguard Worker             } else {
842*c8dee2aaSAndroid Build Coastguard Worker                 gen->builder()->swizzle_copy_stack_to_slots(fixedOffset, swizzle, swizzle.size());
843*c8dee2aaSAndroid Build Coastguard Worker             }
844*c8dee2aaSAndroid Build Coastguard Worker         }
845*c8dee2aaSAndroid Build Coastguard Worker         if (gen->shouldWriteTraceOps()) {
846*c8dee2aaSAndroid Build Coastguard Worker             if (dynamicOffset) {
847*c8dee2aaSAndroid Build Coastguard Worker                 gen->builder()->trace_var_indirect(gen->traceMaskStackID(),
848*c8dee2aaSAndroid Build Coastguard Worker                                                    fixedOffset,
849*c8dee2aaSAndroid Build Coastguard Worker                                                    dynamicOffset->stackID(),
850*c8dee2aaSAndroid Build Coastguard Worker                                                    this->fixedSlotRange(gen));
851*c8dee2aaSAndroid Build Coastguard Worker             } else {
852*c8dee2aaSAndroid Build Coastguard Worker                 gen->builder()->trace_var(gen->traceMaskStackID(), fixedOffset);
853*c8dee2aaSAndroid Build Coastguard Worker             }
854*c8dee2aaSAndroid Build Coastguard Worker         }
855*c8dee2aaSAndroid Build Coastguard Worker         return true;
856*c8dee2aaSAndroid Build Coastguard Worker     }
857*c8dee2aaSAndroid Build Coastguard Worker 
858*c8dee2aaSAndroid Build Coastguard Worker private:
859*c8dee2aaSAndroid Build Coastguard Worker     const Variable* fVariable;
860*c8dee2aaSAndroid Build Coastguard Worker };
861*c8dee2aaSAndroid Build Coastguard Worker 
862*c8dee2aaSAndroid Build Coastguard Worker class ImmutableLValue final : public LValue {
863*c8dee2aaSAndroid Build Coastguard Worker public:
ImmutableLValue(const Variable * v)864*c8dee2aaSAndroid Build Coastguard Worker     explicit ImmutableLValue(const Variable* v) : fVariable(v) {}
865*c8dee2aaSAndroid Build Coastguard Worker 
isWritable() const866*c8dee2aaSAndroid Build Coastguard Worker     bool isWritable() const override {
867*c8dee2aaSAndroid Build Coastguard Worker         return false;
868*c8dee2aaSAndroid Build Coastguard Worker     }
869*c8dee2aaSAndroid Build Coastguard Worker 
fixedSlotRange(Generator * gen)870*c8dee2aaSAndroid Build Coastguard Worker     SlotRange fixedSlotRange(Generator* gen) override {
871*c8dee2aaSAndroid Build Coastguard Worker         return gen->getImmutableSlots(*fVariable);
872*c8dee2aaSAndroid Build Coastguard Worker     }
873*c8dee2aaSAndroid Build Coastguard Worker 
dynamicSlotRange()874*c8dee2aaSAndroid Build Coastguard Worker     AutoStack* dynamicSlotRange() override {
875*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
876*c8dee2aaSAndroid Build Coastguard Worker     }
877*c8dee2aaSAndroid Build Coastguard Worker 
push(Generator * gen,SlotRange fixedOffset,AutoStack * dynamicOffset,SkSpan<const int8_t> swizzle)878*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool push(Generator* gen,
879*c8dee2aaSAndroid Build Coastguard Worker                             SlotRange fixedOffset,
880*c8dee2aaSAndroid Build Coastguard Worker                             AutoStack* dynamicOffset,
881*c8dee2aaSAndroid Build Coastguard Worker                             SkSpan<const int8_t> swizzle) override {
882*c8dee2aaSAndroid Build Coastguard Worker         if (dynamicOffset) {
883*c8dee2aaSAndroid Build Coastguard Worker             gen->builder()->push_immutable_indirect(fixedOffset, dynamicOffset->stackID(),
884*c8dee2aaSAndroid Build Coastguard Worker                                                     this->fixedSlotRange(gen));
885*c8dee2aaSAndroid Build Coastguard Worker         } else {
886*c8dee2aaSAndroid Build Coastguard Worker             gen->builder()->push_immutable(fixedOffset);
887*c8dee2aaSAndroid Build Coastguard Worker         }
888*c8dee2aaSAndroid Build Coastguard Worker         if (!swizzle.empty()) {
889*c8dee2aaSAndroid Build Coastguard Worker             gen->builder()->swizzle(fixedOffset.count, swizzle);
890*c8dee2aaSAndroid Build Coastguard Worker         }
891*c8dee2aaSAndroid Build Coastguard Worker         return true;
892*c8dee2aaSAndroid Build Coastguard Worker     }
893*c8dee2aaSAndroid Build Coastguard Worker 
store(Generator * gen,SlotRange fixedOffset,AutoStack * dynamicOffset,SkSpan<const int8_t> swizzle)894*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool store(Generator* gen,
895*c8dee2aaSAndroid Build Coastguard Worker                              SlotRange fixedOffset,
896*c8dee2aaSAndroid Build Coastguard Worker                              AutoStack* dynamicOffset,
897*c8dee2aaSAndroid Build Coastguard Worker                              SkSpan<const int8_t> swizzle) override {
898*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGFAIL("immutable values cannot be stored into");
899*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
900*c8dee2aaSAndroid Build Coastguard Worker     }
901*c8dee2aaSAndroid Build Coastguard Worker 
902*c8dee2aaSAndroid Build Coastguard Worker private:
903*c8dee2aaSAndroid Build Coastguard Worker     const Variable* fVariable;
904*c8dee2aaSAndroid Build Coastguard Worker };
905*c8dee2aaSAndroid Build Coastguard Worker 
906*c8dee2aaSAndroid Build Coastguard Worker class SwizzleLValue final : public LValue {
907*c8dee2aaSAndroid Build Coastguard Worker public:
SwizzleLValue(std::unique_ptr<LValue> p,const ComponentArray & c)908*c8dee2aaSAndroid Build Coastguard Worker     explicit SwizzleLValue(std::unique_ptr<LValue> p, const ComponentArray& c)
909*c8dee2aaSAndroid Build Coastguard Worker             : fParent(std::move(p))
910*c8dee2aaSAndroid Build Coastguard Worker             , fComponents(c) {
911*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!fComponents.empty() && fComponents.size() <= 4);
912*c8dee2aaSAndroid Build Coastguard Worker     }
913*c8dee2aaSAndroid Build Coastguard Worker 
isWritable() const914*c8dee2aaSAndroid Build Coastguard Worker     bool isWritable() const override {
915*c8dee2aaSAndroid Build Coastguard Worker         return fParent->isWritable();
916*c8dee2aaSAndroid Build Coastguard Worker     }
917*c8dee2aaSAndroid Build Coastguard Worker 
fixedSlotRange(Generator * gen)918*c8dee2aaSAndroid Build Coastguard Worker     SlotRange fixedSlotRange(Generator* gen) override {
919*c8dee2aaSAndroid Build Coastguard Worker         return fParent->fixedSlotRange(gen);
920*c8dee2aaSAndroid Build Coastguard Worker     }
921*c8dee2aaSAndroid Build Coastguard Worker 
dynamicSlotRange()922*c8dee2aaSAndroid Build Coastguard Worker     AutoStack* dynamicSlotRange() override {
923*c8dee2aaSAndroid Build Coastguard Worker         return fParent->dynamicSlotRange();
924*c8dee2aaSAndroid Build Coastguard Worker     }
925*c8dee2aaSAndroid Build Coastguard Worker 
swizzle()926*c8dee2aaSAndroid Build Coastguard Worker     SkSpan<const int8_t> swizzle() override {
927*c8dee2aaSAndroid Build Coastguard Worker         return fComponents;
928*c8dee2aaSAndroid Build Coastguard Worker     }
929*c8dee2aaSAndroid Build Coastguard Worker 
push(Generator * gen,SlotRange fixedOffset,AutoStack * dynamicOffset,SkSpan<const int8_t> swizzle)930*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool push(Generator* gen,
931*c8dee2aaSAndroid Build Coastguard Worker                             SlotRange fixedOffset,
932*c8dee2aaSAndroid Build Coastguard Worker                             AutoStack* dynamicOffset,
933*c8dee2aaSAndroid Build Coastguard Worker                             SkSpan<const int8_t> swizzle) override {
934*c8dee2aaSAndroid Build Coastguard Worker         if (!swizzle.empty()) {
935*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAIL("swizzle-of-a-swizzle should have been folded out in front end");
936*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
937*c8dee2aaSAndroid Build Coastguard Worker         }
938*c8dee2aaSAndroid Build Coastguard Worker         return fParent->push(gen, fixedOffset, dynamicOffset, fComponents);
939*c8dee2aaSAndroid Build Coastguard Worker     }
940*c8dee2aaSAndroid Build Coastguard Worker 
store(Generator * gen,SlotRange fixedOffset,AutoStack * dynamicOffset,SkSpan<const int8_t> swizzle)941*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool store(Generator* gen,
942*c8dee2aaSAndroid Build Coastguard Worker                              SlotRange fixedOffset,
943*c8dee2aaSAndroid Build Coastguard Worker                              AutoStack* dynamicOffset,
944*c8dee2aaSAndroid Build Coastguard Worker                              SkSpan<const int8_t> swizzle) override {
945*c8dee2aaSAndroid Build Coastguard Worker         if (!swizzle.empty()) {
946*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAIL("swizzle-of-a-swizzle should have been folded out in front end");
947*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
948*c8dee2aaSAndroid Build Coastguard Worker         }
949*c8dee2aaSAndroid Build Coastguard Worker         return fParent->store(gen, fixedOffset, dynamicOffset, fComponents);
950*c8dee2aaSAndroid Build Coastguard Worker     }
951*c8dee2aaSAndroid Build Coastguard Worker 
952*c8dee2aaSAndroid Build Coastguard Worker private:
953*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<LValue> fParent;
954*c8dee2aaSAndroid Build Coastguard Worker     const ComponentArray& fComponents;
955*c8dee2aaSAndroid Build Coastguard Worker };
956*c8dee2aaSAndroid Build Coastguard Worker 
957*c8dee2aaSAndroid Build Coastguard Worker class UnownedLValueSlice : public LValue {
958*c8dee2aaSAndroid Build Coastguard Worker public:
UnownedLValueSlice(LValue * p,int initialSlot,int numSlots)959*c8dee2aaSAndroid Build Coastguard Worker     explicit UnownedLValueSlice(LValue* p, int initialSlot, int numSlots)
960*c8dee2aaSAndroid Build Coastguard Worker             : fParent(p)
961*c8dee2aaSAndroid Build Coastguard Worker             , fInitialSlot(initialSlot)
962*c8dee2aaSAndroid Build Coastguard Worker             , fNumSlots(numSlots) {
963*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fInitialSlot >= 0);
964*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fNumSlots > 0);
965*c8dee2aaSAndroid Build Coastguard Worker     }
966*c8dee2aaSAndroid Build Coastguard Worker 
isWritable() const967*c8dee2aaSAndroid Build Coastguard Worker     bool isWritable() const override {
968*c8dee2aaSAndroid Build Coastguard Worker         return fParent->isWritable();
969*c8dee2aaSAndroid Build Coastguard Worker     }
970*c8dee2aaSAndroid Build Coastguard Worker 
fixedSlotRange(Generator * gen)971*c8dee2aaSAndroid Build Coastguard Worker     SlotRange fixedSlotRange(Generator* gen) override {
972*c8dee2aaSAndroid Build Coastguard Worker         SlotRange range = fParent->fixedSlotRange(gen);
973*c8dee2aaSAndroid Build Coastguard Worker         SlotRange adjusted = range;
974*c8dee2aaSAndroid Build Coastguard Worker         adjusted.index += fInitialSlot;
975*c8dee2aaSAndroid Build Coastguard Worker         adjusted.count = fNumSlots;
976*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT((adjusted.index + adjusted.count) <= (range.index + range.count));
977*c8dee2aaSAndroid Build Coastguard Worker         return adjusted;
978*c8dee2aaSAndroid Build Coastguard Worker     }
979*c8dee2aaSAndroid Build Coastguard Worker 
dynamicSlotRange()980*c8dee2aaSAndroid Build Coastguard Worker     AutoStack* dynamicSlotRange() override {
981*c8dee2aaSAndroid Build Coastguard Worker         return fParent->dynamicSlotRange();
982*c8dee2aaSAndroid Build Coastguard Worker     }
983*c8dee2aaSAndroid Build Coastguard Worker 
push(Generator * gen,SlotRange fixedOffset,AutoStack * dynamicOffset,SkSpan<const int8_t> swizzle)984*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool push(Generator* gen,
985*c8dee2aaSAndroid Build Coastguard Worker                             SlotRange fixedOffset,
986*c8dee2aaSAndroid Build Coastguard Worker                             AutoStack* dynamicOffset,
987*c8dee2aaSAndroid Build Coastguard Worker                             SkSpan<const int8_t> swizzle) override {
988*c8dee2aaSAndroid Build Coastguard Worker         return fParent->push(gen, fixedOffset, dynamicOffset, swizzle);
989*c8dee2aaSAndroid Build Coastguard Worker     }
990*c8dee2aaSAndroid Build Coastguard Worker 
store(Generator * gen,SlotRange fixedOffset,AutoStack * dynamicOffset,SkSpan<const int8_t> swizzle)991*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool store(Generator* gen,
992*c8dee2aaSAndroid Build Coastguard Worker                              SlotRange fixedOffset,
993*c8dee2aaSAndroid Build Coastguard Worker                              AutoStack* dynamicOffset,
994*c8dee2aaSAndroid Build Coastguard Worker                              SkSpan<const int8_t> swizzle) override {
995*c8dee2aaSAndroid Build Coastguard Worker         return fParent->store(gen, fixedOffset, dynamicOffset, swizzle);
996*c8dee2aaSAndroid Build Coastguard Worker     }
997*c8dee2aaSAndroid Build Coastguard Worker 
998*c8dee2aaSAndroid Build Coastguard Worker protected:
999*c8dee2aaSAndroid Build Coastguard Worker     LValue* fParent;
1000*c8dee2aaSAndroid Build Coastguard Worker 
1001*c8dee2aaSAndroid Build Coastguard Worker private:
1002*c8dee2aaSAndroid Build Coastguard Worker     int fInitialSlot = 0;
1003*c8dee2aaSAndroid Build Coastguard Worker     int fNumSlots = 0;
1004*c8dee2aaSAndroid Build Coastguard Worker };
1005*c8dee2aaSAndroid Build Coastguard Worker 
1006*c8dee2aaSAndroid Build Coastguard Worker class LValueSlice final : public UnownedLValueSlice {
1007*c8dee2aaSAndroid Build Coastguard Worker public:
LValueSlice(std::unique_ptr<LValue> p,int initialSlot,int numSlots)1008*c8dee2aaSAndroid Build Coastguard Worker     explicit LValueSlice(std::unique_ptr<LValue> p, int initialSlot, int numSlots)
1009*c8dee2aaSAndroid Build Coastguard Worker             : UnownedLValueSlice(p.release(), initialSlot, numSlots) {}
1010*c8dee2aaSAndroid Build Coastguard Worker 
~LValueSlice()1011*c8dee2aaSAndroid Build Coastguard Worker     ~LValueSlice() override {
1012*c8dee2aaSAndroid Build Coastguard Worker         delete fParent;
1013*c8dee2aaSAndroid Build Coastguard Worker     }
1014*c8dee2aaSAndroid Build Coastguard Worker };
1015*c8dee2aaSAndroid Build Coastguard Worker 
1016*c8dee2aaSAndroid Build Coastguard Worker class DynamicIndexLValue final : public LValue {
1017*c8dee2aaSAndroid Build Coastguard Worker public:
DynamicIndexLValue(std::unique_ptr<LValue> p,const IndexExpression & i)1018*c8dee2aaSAndroid Build Coastguard Worker     explicit DynamicIndexLValue(std::unique_ptr<LValue> p, const IndexExpression& i)
1019*c8dee2aaSAndroid Build Coastguard Worker             : fParent(std::move(p))
1020*c8dee2aaSAndroid Build Coastguard Worker             , fIndexExpr(&i) {
1021*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fIndexExpr->index()->type().isInteger());
1022*c8dee2aaSAndroid Build Coastguard Worker     }
1023*c8dee2aaSAndroid Build Coastguard Worker 
~DynamicIndexLValue()1024*c8dee2aaSAndroid Build Coastguard Worker     ~DynamicIndexLValue() override {
1025*c8dee2aaSAndroid Build Coastguard Worker         if (fDedicatedStack.has_value()) {
1026*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(fGenerator);
1027*c8dee2aaSAndroid Build Coastguard Worker 
1028*c8dee2aaSAndroid Build Coastguard Worker             // Jettison the index expression.
1029*c8dee2aaSAndroid Build Coastguard Worker             fDedicatedStack->enter();
1030*c8dee2aaSAndroid Build Coastguard Worker             fGenerator->discardExpression(/*slots=*/1);
1031*c8dee2aaSAndroid Build Coastguard Worker             fDedicatedStack->exit();
1032*c8dee2aaSAndroid Build Coastguard Worker         }
1033*c8dee2aaSAndroid Build Coastguard Worker     }
1034*c8dee2aaSAndroid Build Coastguard Worker 
isWritable() const1035*c8dee2aaSAndroid Build Coastguard Worker     bool isWritable() const override {
1036*c8dee2aaSAndroid Build Coastguard Worker         return fParent->isWritable();
1037*c8dee2aaSAndroid Build Coastguard Worker     }
1038*c8dee2aaSAndroid Build Coastguard Worker 
evaluateDynamicIndices(Generator * gen)1039*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool evaluateDynamicIndices(Generator* gen) {
1040*c8dee2aaSAndroid Build Coastguard Worker         // The index must only be computed once; the index-expression could have side effects.
1041*c8dee2aaSAndroid Build Coastguard Worker         // Once it has been computed, the offset lives on `fDedicatedStack`.
1042*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!fDedicatedStack.has_value());
1043*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!fGenerator);
1044*c8dee2aaSAndroid Build Coastguard Worker         fGenerator = gen;
1045*c8dee2aaSAndroid Build Coastguard Worker         fDedicatedStack.emplace(fGenerator);
1046*c8dee2aaSAndroid Build Coastguard Worker 
1047*c8dee2aaSAndroid Build Coastguard Worker         if (!fParent->swizzle().empty()) {
1048*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAIL("an indexed-swizzle should have been handled by RewriteIndexedSwizzle");
1049*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
1050*c8dee2aaSAndroid Build Coastguard Worker         }
1051*c8dee2aaSAndroid Build Coastguard Worker 
1052*c8dee2aaSAndroid Build Coastguard Worker         // Push the index expression onto the dedicated stack.
1053*c8dee2aaSAndroid Build Coastguard Worker         fDedicatedStack->enter();
1054*c8dee2aaSAndroid Build Coastguard Worker         if (!fGenerator->pushExpression(*fIndexExpr->index())) {
1055*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
1056*c8dee2aaSAndroid Build Coastguard Worker         }
1057*c8dee2aaSAndroid Build Coastguard Worker 
1058*c8dee2aaSAndroid Build Coastguard Worker         // Multiply the index-expression result by the per-value slot count.
1059*c8dee2aaSAndroid Build Coastguard Worker         int slotCount = fIndexExpr->type().slotCount();
1060*c8dee2aaSAndroid Build Coastguard Worker         if (slotCount != 1) {
1061*c8dee2aaSAndroid Build Coastguard Worker             fGenerator->builder()->push_constant_i(fIndexExpr->type().slotCount());
1062*c8dee2aaSAndroid Build Coastguard Worker             fGenerator->builder()->binary_op(BuilderOp::mul_n_ints, 1);
1063*c8dee2aaSAndroid Build Coastguard Worker         }
1064*c8dee2aaSAndroid Build Coastguard Worker 
1065*c8dee2aaSAndroid Build Coastguard Worker         // Check to see if a parent LValue already has a dynamic index. If so, we need to
1066*c8dee2aaSAndroid Build Coastguard Worker         // incorporate its value into our own.
1067*c8dee2aaSAndroid Build Coastguard Worker         if (AutoStack* parentDynamicIndexStack = fParent->dynamicSlotRange()) {
1068*c8dee2aaSAndroid Build Coastguard Worker             parentDynamicIndexStack->pushClone(/*slots=*/1);
1069*c8dee2aaSAndroid Build Coastguard Worker             fGenerator->builder()->binary_op(BuilderOp::add_n_ints, 1);
1070*c8dee2aaSAndroid Build Coastguard Worker         }
1071*c8dee2aaSAndroid Build Coastguard Worker         fDedicatedStack->exit();
1072*c8dee2aaSAndroid Build Coastguard Worker         return true;
1073*c8dee2aaSAndroid Build Coastguard Worker     }
1074*c8dee2aaSAndroid Build Coastguard Worker 
fixedSlotRange(Generator * gen)1075*c8dee2aaSAndroid Build Coastguard Worker     SlotRange fixedSlotRange(Generator* gen) override {
1076*c8dee2aaSAndroid Build Coastguard Worker         // Compute the fixed slot range as if we are indexing into position zero.
1077*c8dee2aaSAndroid Build Coastguard Worker         SlotRange range = fParent->fixedSlotRange(gen);
1078*c8dee2aaSAndroid Build Coastguard Worker         range.count = fIndexExpr->type().slotCount();
1079*c8dee2aaSAndroid Build Coastguard Worker         return range;
1080*c8dee2aaSAndroid Build Coastguard Worker     }
1081*c8dee2aaSAndroid Build Coastguard Worker 
dynamicSlotRange()1082*c8dee2aaSAndroid Build Coastguard Worker     AutoStack* dynamicSlotRange() override {
1083*c8dee2aaSAndroid Build Coastguard Worker         // We incorporated any parent dynamic offsets when `evaluateDynamicIndices` was called.
1084*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fDedicatedStack.has_value());
1085*c8dee2aaSAndroid Build Coastguard Worker         return &*fDedicatedStack;
1086*c8dee2aaSAndroid Build Coastguard Worker     }
1087*c8dee2aaSAndroid Build Coastguard Worker 
push(Generator * gen,SlotRange fixedOffset,AutoStack * dynamicOffset,SkSpan<const int8_t> swizzle)1088*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool push(Generator* gen,
1089*c8dee2aaSAndroid Build Coastguard Worker                             SlotRange fixedOffset,
1090*c8dee2aaSAndroid Build Coastguard Worker                             AutoStack* dynamicOffset,
1091*c8dee2aaSAndroid Build Coastguard Worker                             SkSpan<const int8_t> swizzle) override {
1092*c8dee2aaSAndroid Build Coastguard Worker         return fParent->push(gen, fixedOffset, dynamicOffset, swizzle);
1093*c8dee2aaSAndroid Build Coastguard Worker     }
1094*c8dee2aaSAndroid Build Coastguard Worker 
store(Generator * gen,SlotRange fixedOffset,AutoStack * dynamicOffset,SkSpan<const int8_t> swizzle)1095*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] bool store(Generator* gen,
1096*c8dee2aaSAndroid Build Coastguard Worker                              SlotRange fixedOffset,
1097*c8dee2aaSAndroid Build Coastguard Worker                              AutoStack* dynamicOffset,
1098*c8dee2aaSAndroid Build Coastguard Worker                              SkSpan<const int8_t> swizzle) override {
1099*c8dee2aaSAndroid Build Coastguard Worker         return fParent->store(gen, fixedOffset, dynamicOffset, swizzle);
1100*c8dee2aaSAndroid Build Coastguard Worker     }
1101*c8dee2aaSAndroid Build Coastguard Worker 
1102*c8dee2aaSAndroid Build Coastguard Worker private:
1103*c8dee2aaSAndroid Build Coastguard Worker     Generator* fGenerator = nullptr;
1104*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<LValue> fParent;
1105*c8dee2aaSAndroid Build Coastguard Worker     std::optional<AutoStack> fDedicatedStack;
1106*c8dee2aaSAndroid Build Coastguard Worker     const IndexExpression* fIndexExpr = nullptr;
1107*c8dee2aaSAndroid Build Coastguard Worker };
1108*c8dee2aaSAndroid Build Coastguard Worker 
addSlotDebugInfoForGroup(const std::string & varName,const Type & type,Position pos,int * groupIndex,bool isFunctionReturnValue)1109*c8dee2aaSAndroid Build Coastguard Worker void SlotManager::addSlotDebugInfoForGroup(const std::string& varName,
1110*c8dee2aaSAndroid Build Coastguard Worker                                            const Type& type,
1111*c8dee2aaSAndroid Build Coastguard Worker                                            Position pos,
1112*c8dee2aaSAndroid Build Coastguard Worker                                            int* groupIndex,
1113*c8dee2aaSAndroid Build Coastguard Worker                                            bool isFunctionReturnValue) {
1114*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fSlotDebugInfo);
1115*c8dee2aaSAndroid Build Coastguard Worker     switch (type.typeKind()) {
1116*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kArray: {
1117*c8dee2aaSAndroid Build Coastguard Worker             int nslots = type.columns();
1118*c8dee2aaSAndroid Build Coastguard Worker             const Type& elemType = type.componentType();
1119*c8dee2aaSAndroid Build Coastguard Worker             for (int slot = 0; slot < nslots; ++slot) {
1120*c8dee2aaSAndroid Build Coastguard Worker                 this->addSlotDebugInfoForGroup(varName + "[" + std::to_string(slot) + "]", elemType,
1121*c8dee2aaSAndroid Build Coastguard Worker                                                pos, groupIndex, isFunctionReturnValue);
1122*c8dee2aaSAndroid Build Coastguard Worker             }
1123*c8dee2aaSAndroid Build Coastguard Worker             break;
1124*c8dee2aaSAndroid Build Coastguard Worker         }
1125*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kStruct: {
1126*c8dee2aaSAndroid Build Coastguard Worker             for (const Field& field : type.fields()) {
1127*c8dee2aaSAndroid Build Coastguard Worker                 this->addSlotDebugInfoForGroup(varName + "." + std::string(field.fName),
1128*c8dee2aaSAndroid Build Coastguard Worker                                                *field.fType, pos, groupIndex,
1129*c8dee2aaSAndroid Build Coastguard Worker                                                isFunctionReturnValue);
1130*c8dee2aaSAndroid Build Coastguard Worker             }
1131*c8dee2aaSAndroid Build Coastguard Worker             break;
1132*c8dee2aaSAndroid Build Coastguard Worker         }
1133*c8dee2aaSAndroid Build Coastguard Worker         default:
1134*c8dee2aaSAndroid Build Coastguard Worker             SkASSERTF(0, "unsupported slot type %d", (int)type.typeKind());
1135*c8dee2aaSAndroid Build Coastguard Worker             [[fallthrough]];
1136*c8dee2aaSAndroid Build Coastguard Worker 
1137*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kScalar:
1138*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kVector:
1139*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kMatrix: {
1140*c8dee2aaSAndroid Build Coastguard Worker             Type::NumberKind numberKind = type.componentType().numberKind();
1141*c8dee2aaSAndroid Build Coastguard Worker             int nslots = type.slotCount();
1142*c8dee2aaSAndroid Build Coastguard Worker 
1143*c8dee2aaSAndroid Build Coastguard Worker             for (int slot = 0; slot < nslots; ++slot) {
1144*c8dee2aaSAndroid Build Coastguard Worker                 SlotDebugInfo slotInfo;
1145*c8dee2aaSAndroid Build Coastguard Worker                 slotInfo.name = varName;
1146*c8dee2aaSAndroid Build Coastguard Worker                 slotInfo.columns = type.columns();
1147*c8dee2aaSAndroid Build Coastguard Worker                 slotInfo.rows = type.rows();
1148*c8dee2aaSAndroid Build Coastguard Worker                 slotInfo.componentIndex = slot;
1149*c8dee2aaSAndroid Build Coastguard Worker                 slotInfo.groupIndex = (*groupIndex)++;
1150*c8dee2aaSAndroid Build Coastguard Worker                 slotInfo.numberKind = numberKind;
1151*c8dee2aaSAndroid Build Coastguard Worker                 slotInfo.pos = pos;
1152*c8dee2aaSAndroid Build Coastguard Worker                 slotInfo.fnReturnValue = isFunctionReturnValue ? 1 : -1;
1153*c8dee2aaSAndroid Build Coastguard Worker                 fSlotDebugInfo->push_back(std::move(slotInfo));
1154*c8dee2aaSAndroid Build Coastguard Worker             }
1155*c8dee2aaSAndroid Build Coastguard Worker             break;
1156*c8dee2aaSAndroid Build Coastguard Worker         }
1157*c8dee2aaSAndroid Build Coastguard Worker     }
1158*c8dee2aaSAndroid Build Coastguard Worker }
1159*c8dee2aaSAndroid Build Coastguard Worker 
addSlotDebugInfo(const std::string & varName,const Type & type,Position pos,bool isFunctionReturnValue)1160*c8dee2aaSAndroid Build Coastguard Worker void SlotManager::addSlotDebugInfo(const std::string& varName,
1161*c8dee2aaSAndroid Build Coastguard Worker                                    const Type& type,
1162*c8dee2aaSAndroid Build Coastguard Worker                                    Position pos,
1163*c8dee2aaSAndroid Build Coastguard Worker                                    bool isFunctionReturnValue) {
1164*c8dee2aaSAndroid Build Coastguard Worker     int groupIndex = 0;
1165*c8dee2aaSAndroid Build Coastguard Worker     this->addSlotDebugInfoForGroup(varName, type, pos, &groupIndex, isFunctionReturnValue);
1166*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT((size_t)groupIndex == type.slotCount());
1167*c8dee2aaSAndroid Build Coastguard Worker }
1168*c8dee2aaSAndroid Build Coastguard Worker 
createSlots(std::string name,const Type & type,Position pos,bool isFunctionReturnValue)1169*c8dee2aaSAndroid Build Coastguard Worker SlotRange SlotManager::createSlots(std::string name,
1170*c8dee2aaSAndroid Build Coastguard Worker                                    const Type& type,
1171*c8dee2aaSAndroid Build Coastguard Worker                                    Position pos,
1172*c8dee2aaSAndroid Build Coastguard Worker                                    bool isFunctionReturnValue) {
1173*c8dee2aaSAndroid Build Coastguard Worker     size_t nslots = type.slotCount();
1174*c8dee2aaSAndroid Build Coastguard Worker     if (nslots == 0) {
1175*c8dee2aaSAndroid Build Coastguard Worker         return {};
1176*c8dee2aaSAndroid Build Coastguard Worker     }
1177*c8dee2aaSAndroid Build Coastguard Worker     if (fSlotDebugInfo) {
1178*c8dee2aaSAndroid Build Coastguard Worker         // Our debug slot-info table should have the same length as the actual slot table.
1179*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fSlotDebugInfo->size() == (size_t)fSlotCount);
1180*c8dee2aaSAndroid Build Coastguard Worker 
1181*c8dee2aaSAndroid Build Coastguard Worker         // Append slot names and types to our debug slot-info table.
1182*c8dee2aaSAndroid Build Coastguard Worker         fSlotDebugInfo->reserve(fSlotCount + nslots);
1183*c8dee2aaSAndroid Build Coastguard Worker         this->addSlotDebugInfo(name, type, pos, isFunctionReturnValue);
1184*c8dee2aaSAndroid Build Coastguard Worker 
1185*c8dee2aaSAndroid Build Coastguard Worker         // Confirm that we added the expected number of slots.
1186*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fSlotDebugInfo->size() == (size_t)(fSlotCount + nslots));
1187*c8dee2aaSAndroid Build Coastguard Worker     }
1188*c8dee2aaSAndroid Build Coastguard Worker 
1189*c8dee2aaSAndroid Build Coastguard Worker     SlotRange result = {fSlotCount, (int)nslots};
1190*c8dee2aaSAndroid Build Coastguard Worker     fSlotCount += nslots;
1191*c8dee2aaSAndroid Build Coastguard Worker     return result;
1192*c8dee2aaSAndroid Build Coastguard Worker }
1193*c8dee2aaSAndroid Build Coastguard Worker 
mapVariableToSlots(const Variable & v,SlotRange range)1194*c8dee2aaSAndroid Build Coastguard Worker std::optional<SlotRange> SlotManager::mapVariableToSlots(const Variable& v, SlotRange range) {
1195*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(v.type().slotCount() == SkToSizeT(range.count));
1196*c8dee2aaSAndroid Build Coastguard Worker     const SlotRange* existingEntry = fSlotMap.find(&v);
1197*c8dee2aaSAndroid Build Coastguard Worker     std::optional<SlotRange> originalRange = existingEntry ? std::optional(*existingEntry)
1198*c8dee2aaSAndroid Build Coastguard Worker                                                            : std::nullopt;
1199*c8dee2aaSAndroid Build Coastguard Worker     fSlotMap.set(&v, range);
1200*c8dee2aaSAndroid Build Coastguard Worker     return originalRange;
1201*c8dee2aaSAndroid Build Coastguard Worker }
1202*c8dee2aaSAndroid Build Coastguard Worker 
unmapVariableSlots(const Variable & v)1203*c8dee2aaSAndroid Build Coastguard Worker void SlotManager::unmapVariableSlots(const Variable& v) {
1204*c8dee2aaSAndroid Build Coastguard Worker     fSlotMap.remove(&v);
1205*c8dee2aaSAndroid Build Coastguard Worker }
1206*c8dee2aaSAndroid Build Coastguard Worker 
getVariableSlots(const Variable & v)1207*c8dee2aaSAndroid Build Coastguard Worker SlotRange SlotManager::getVariableSlots(const Variable& v) {
1208*c8dee2aaSAndroid Build Coastguard Worker     SlotRange* entry = fSlotMap.find(&v);
1209*c8dee2aaSAndroid Build Coastguard Worker     if (entry != nullptr) {
1210*c8dee2aaSAndroid Build Coastguard Worker         return *entry;
1211*c8dee2aaSAndroid Build Coastguard Worker     }
1212*c8dee2aaSAndroid Build Coastguard Worker     SlotRange range = this->createSlots(std::string(v.name()),
1213*c8dee2aaSAndroid Build Coastguard Worker                                         v.type(),
1214*c8dee2aaSAndroid Build Coastguard Worker                                         v.fPosition,
1215*c8dee2aaSAndroid Build Coastguard Worker                                         /*isFunctionReturnValue=*/false);
1216*c8dee2aaSAndroid Build Coastguard Worker     this->mapVariableToSlots(v, range);
1217*c8dee2aaSAndroid Build Coastguard Worker     return range;
1218*c8dee2aaSAndroid Build Coastguard Worker }
1219*c8dee2aaSAndroid Build Coastguard Worker 
getFunctionSlots(const IRNode & callSite,const FunctionDeclaration & f)1220*c8dee2aaSAndroid Build Coastguard Worker SlotRange SlotManager::getFunctionSlots(const IRNode& callSite, const FunctionDeclaration& f) {
1221*c8dee2aaSAndroid Build Coastguard Worker     SlotRange* entry = fSlotMap.find(&callSite);
1222*c8dee2aaSAndroid Build Coastguard Worker     if (entry != nullptr) {
1223*c8dee2aaSAndroid Build Coastguard Worker         return *entry;
1224*c8dee2aaSAndroid Build Coastguard Worker     }
1225*c8dee2aaSAndroid Build Coastguard Worker     SlotRange range = this->createSlots("[" + std::string(f.name()) + "].result",
1226*c8dee2aaSAndroid Build Coastguard Worker                                         f.returnType(),
1227*c8dee2aaSAndroid Build Coastguard Worker                                         f.fPosition,
1228*c8dee2aaSAndroid Build Coastguard Worker                                         /*isFunctionReturnValue=*/true);
1229*c8dee2aaSAndroid Build Coastguard Worker     fSlotMap.set(&callSite, range);
1230*c8dee2aaSAndroid Build Coastguard Worker     return range;
1231*c8dee2aaSAndroid Build Coastguard Worker }
1232*c8dee2aaSAndroid Build Coastguard Worker 
is_sliceable_swizzle(SkSpan<const int8_t> components)1233*c8dee2aaSAndroid Build Coastguard Worker static bool is_sliceable_swizzle(SkSpan<const int8_t> components) {
1234*c8dee2aaSAndroid Build Coastguard Worker     // Determine if the swizzle rearranges its elements, or if it's a simple subset of its elements.
1235*c8dee2aaSAndroid Build Coastguard Worker     // (A simple subset would be a sequential non-repeating range of components, like `.xyz` or
1236*c8dee2aaSAndroid Build Coastguard Worker     // `.yzw` or `.z`, but not `.xx` or `.xz`, which can be accessed as a slice of the variable.)
1237*c8dee2aaSAndroid Build Coastguard Worker     for (size_t index = 1; index < components.size(); ++index) {
1238*c8dee2aaSAndroid Build Coastguard Worker         if (components[index] != int8_t(components[0] + index)) {
1239*c8dee2aaSAndroid Build Coastguard Worker             return false;
1240*c8dee2aaSAndroid Build Coastguard Worker         }
1241*c8dee2aaSAndroid Build Coastguard Worker     }
1242*c8dee2aaSAndroid Build Coastguard Worker     return true;
1243*c8dee2aaSAndroid Build Coastguard Worker }
1244*c8dee2aaSAndroid Build Coastguard Worker 
makeLValue(const Expression & e,bool allowScratch)1245*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<LValue> Generator::makeLValue(const Expression& e, bool allowScratch) {
1246*c8dee2aaSAndroid Build Coastguard Worker     if (e.is<VariableReference>()) {
1247*c8dee2aaSAndroid Build Coastguard Worker         const Variable* variable = e.as<VariableReference>().variable();
1248*c8dee2aaSAndroid Build Coastguard Worker         if (fImmutableVariables.contains(variable)) {
1249*c8dee2aaSAndroid Build Coastguard Worker             return std::make_unique<ImmutableLValue>(variable);
1250*c8dee2aaSAndroid Build Coastguard Worker         }
1251*c8dee2aaSAndroid Build Coastguard Worker         return std::make_unique<VariableLValue>(variable);
1252*c8dee2aaSAndroid Build Coastguard Worker     }
1253*c8dee2aaSAndroid Build Coastguard Worker     if (e.is<Swizzle>()) {
1254*c8dee2aaSAndroid Build Coastguard Worker         const Swizzle& swizzleExpr = e.as<Swizzle>();
1255*c8dee2aaSAndroid Build Coastguard Worker         if (std::unique_ptr<LValue> base = this->makeLValue(*swizzleExpr.base(),
1256*c8dee2aaSAndroid Build Coastguard Worker                                                             allowScratch)) {
1257*c8dee2aaSAndroid Build Coastguard Worker             const ComponentArray& components = swizzleExpr.components();
1258*c8dee2aaSAndroid Build Coastguard Worker             if (is_sliceable_swizzle(components)) {
1259*c8dee2aaSAndroid Build Coastguard Worker                 // If the swizzle is a contiguous subset, we can represent it with a fixed slice.
1260*c8dee2aaSAndroid Build Coastguard Worker                 return std::make_unique<LValueSlice>(std::move(base), components[0],
1261*c8dee2aaSAndroid Build Coastguard Worker                                                      components.size());
1262*c8dee2aaSAndroid Build Coastguard Worker             }
1263*c8dee2aaSAndroid Build Coastguard Worker             return std::make_unique<SwizzleLValue>(std::move(base), components);
1264*c8dee2aaSAndroid Build Coastguard Worker         }
1265*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
1266*c8dee2aaSAndroid Build Coastguard Worker     }
1267*c8dee2aaSAndroid Build Coastguard Worker     if (e.is<FieldAccess>()) {
1268*c8dee2aaSAndroid Build Coastguard Worker         const FieldAccess& fieldExpr = e.as<FieldAccess>();
1269*c8dee2aaSAndroid Build Coastguard Worker         if (std::unique_ptr<LValue> base = this->makeLValue(*fieldExpr.base(),
1270*c8dee2aaSAndroid Build Coastguard Worker                                                             allowScratch)) {
1271*c8dee2aaSAndroid Build Coastguard Worker             // Represent field access with a slice.
1272*c8dee2aaSAndroid Build Coastguard Worker             return std::make_unique<LValueSlice>(std::move(base), fieldExpr.initialSlot(),
1273*c8dee2aaSAndroid Build Coastguard Worker                                                  fieldExpr.type().slotCount());
1274*c8dee2aaSAndroid Build Coastguard Worker         }
1275*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
1276*c8dee2aaSAndroid Build Coastguard Worker     }
1277*c8dee2aaSAndroid Build Coastguard Worker     if (e.is<IndexExpression>()) {
1278*c8dee2aaSAndroid Build Coastguard Worker         const IndexExpression& indexExpr = e.as<IndexExpression>();
1279*c8dee2aaSAndroid Build Coastguard Worker 
1280*c8dee2aaSAndroid Build Coastguard Worker         // If the index base is swizzled (`vec.zyx[idx]`), rewrite it into an equivalent
1281*c8dee2aaSAndroid Build Coastguard Worker         // non-swizzled form (`vec[uint3(2,1,0)[idx]]`).
1282*c8dee2aaSAndroid Build Coastguard Worker         if (std::unique_ptr<Expression> rewritten = Transform::RewriteIndexedSwizzle(fContext,
1283*c8dee2aaSAndroid Build Coastguard Worker                                                                                      indexExpr)) {
1284*c8dee2aaSAndroid Build Coastguard Worker             // Convert the rewritten expression into an lvalue.
1285*c8dee2aaSAndroid Build Coastguard Worker             std::unique_ptr<LValue> lvalue = this->makeLValue(*rewritten, allowScratch);
1286*c8dee2aaSAndroid Build Coastguard Worker             if (!lvalue) {
1287*c8dee2aaSAndroid Build Coastguard Worker                 return nullptr;
1288*c8dee2aaSAndroid Build Coastguard Worker             }
1289*c8dee2aaSAndroid Build Coastguard Worker             // We need to hold onto the rewritten expression for the lifetime of the lvalue.
1290*c8dee2aaSAndroid Build Coastguard Worker             lvalue->fScratchExpression = std::move(rewritten);
1291*c8dee2aaSAndroid Build Coastguard Worker             return lvalue;
1292*c8dee2aaSAndroid Build Coastguard Worker         }
1293*c8dee2aaSAndroid Build Coastguard Worker         if (std::unique_ptr<LValue> base = this->makeLValue(*indexExpr.base(),
1294*c8dee2aaSAndroid Build Coastguard Worker                                                             allowScratch)) {
1295*c8dee2aaSAndroid Build Coastguard Worker             // If the index is a compile-time constant, we can represent it with a fixed slice.
1296*c8dee2aaSAndroid Build Coastguard Worker             SKSL_INT indexValue;
1297*c8dee2aaSAndroid Build Coastguard Worker             if (ConstantFolder::GetConstantInt(*indexExpr.index(), &indexValue)) {
1298*c8dee2aaSAndroid Build Coastguard Worker                 int numSlots = indexExpr.type().slotCount();
1299*c8dee2aaSAndroid Build Coastguard Worker                 return std::make_unique<LValueSlice>(std::move(base), numSlots * indexValue,
1300*c8dee2aaSAndroid Build Coastguard Worker                                                      numSlots);
1301*c8dee2aaSAndroid Build Coastguard Worker             }
1302*c8dee2aaSAndroid Build Coastguard Worker 
1303*c8dee2aaSAndroid Build Coastguard Worker             // Represent non-constant indexing via a dynamic index.
1304*c8dee2aaSAndroid Build Coastguard Worker             auto dynLValue = std::make_unique<DynamicIndexLValue>(std::move(base), indexExpr);
1305*c8dee2aaSAndroid Build Coastguard Worker             return dynLValue->evaluateDynamicIndices(this) ? std::move(dynLValue)
1306*c8dee2aaSAndroid Build Coastguard Worker                                                            : nullptr;
1307*c8dee2aaSAndroid Build Coastguard Worker         }
1308*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
1309*c8dee2aaSAndroid Build Coastguard Worker     }
1310*c8dee2aaSAndroid Build Coastguard Worker     if (allowScratch) {
1311*c8dee2aaSAndroid Build Coastguard Worker         // This path allows us to perform field- and index-accesses on an expression as if it were
1312*c8dee2aaSAndroid Build Coastguard Worker         // an lvalue, but is a temporary and shouldn't be written back to.
1313*c8dee2aaSAndroid Build Coastguard Worker         return std::make_unique<ScratchLValue>(e);
1314*c8dee2aaSAndroid Build Coastguard Worker     }
1315*c8dee2aaSAndroid Build Coastguard Worker     return nullptr;
1316*c8dee2aaSAndroid Build Coastguard Worker }
1317*c8dee2aaSAndroid Build Coastguard Worker 
push(LValue & lvalue)1318*c8dee2aaSAndroid Build Coastguard Worker bool Generator::push(LValue& lvalue) {
1319*c8dee2aaSAndroid Build Coastguard Worker     return lvalue.push(this,
1320*c8dee2aaSAndroid Build Coastguard Worker                        lvalue.fixedSlotRange(this),
1321*c8dee2aaSAndroid Build Coastguard Worker                        lvalue.dynamicSlotRange(),
1322*c8dee2aaSAndroid Build Coastguard Worker                        /*swizzle=*/{});
1323*c8dee2aaSAndroid Build Coastguard Worker }
1324*c8dee2aaSAndroid Build Coastguard Worker 
store(LValue & lvalue)1325*c8dee2aaSAndroid Build Coastguard Worker bool Generator::store(LValue& lvalue) {
1326*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(lvalue.isWritable());
1327*c8dee2aaSAndroid Build Coastguard Worker     return lvalue.store(this,
1328*c8dee2aaSAndroid Build Coastguard Worker                         lvalue.fixedSlotRange(this),
1329*c8dee2aaSAndroid Build Coastguard Worker                         lvalue.dynamicSlotRange(),
1330*c8dee2aaSAndroid Build Coastguard Worker                         /*swizzle=*/{});
1331*c8dee2aaSAndroid Build Coastguard Worker }
1332*c8dee2aaSAndroid Build Coastguard Worker 
getFunctionDebugInfo(const FunctionDeclaration & decl)1333*c8dee2aaSAndroid Build Coastguard Worker int Generator::getFunctionDebugInfo(const FunctionDeclaration& decl) {
1334*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fDebugTrace);
1335*c8dee2aaSAndroid Build Coastguard Worker 
1336*c8dee2aaSAndroid Build Coastguard Worker     std::string name = decl.description();
1337*c8dee2aaSAndroid Build Coastguard Worker 
1338*c8dee2aaSAndroid Build Coastguard Worker     // When generating the debug trace, we typically mark every function as `noinline`. This makes
1339*c8dee2aaSAndroid Build Coastguard Worker     // the trace more confusing, since this isn't in the source program, so remove it.
1340*c8dee2aaSAndroid Build Coastguard Worker     static constexpr std::string_view kNoInline = "noinline ";
1341*c8dee2aaSAndroid Build Coastguard Worker     if (skstd::starts_with(name, kNoInline)) {
1342*c8dee2aaSAndroid Build Coastguard Worker         name = name.substr(kNoInline.size());
1343*c8dee2aaSAndroid Build Coastguard Worker     }
1344*c8dee2aaSAndroid Build Coastguard Worker 
1345*c8dee2aaSAndroid Build Coastguard Worker     // Look for a matching FunctionDebugInfo slot.
1346*c8dee2aaSAndroid Build Coastguard Worker     for (size_t index = 0; index < fDebugTrace->fFuncInfo.size(); ++index) {
1347*c8dee2aaSAndroid Build Coastguard Worker         if (fDebugTrace->fFuncInfo[index].name == name) {
1348*c8dee2aaSAndroid Build Coastguard Worker             return index;
1349*c8dee2aaSAndroid Build Coastguard Worker         }
1350*c8dee2aaSAndroid Build Coastguard Worker     }
1351*c8dee2aaSAndroid Build Coastguard Worker 
1352*c8dee2aaSAndroid Build Coastguard Worker     // We've never called this function before; create a new slot to hold its information.
1353*c8dee2aaSAndroid Build Coastguard Worker     int slot = (int)fDebugTrace->fFuncInfo.size();
1354*c8dee2aaSAndroid Build Coastguard Worker     fDebugTrace->fFuncInfo.push_back(FunctionDebugInfo{std::move(name)});
1355*c8dee2aaSAndroid Build Coastguard Worker     return slot;
1356*c8dee2aaSAndroid Build Coastguard Worker }
1357*c8dee2aaSAndroid Build Coastguard Worker 
createStack()1358*c8dee2aaSAndroid Build Coastguard Worker int Generator::createStack() {
1359*c8dee2aaSAndroid Build Coastguard Worker     if (!fRecycledStacks.empty()) {
1360*c8dee2aaSAndroid Build Coastguard Worker         int stackID = fRecycledStacks.back();
1361*c8dee2aaSAndroid Build Coastguard Worker         fRecycledStacks.pop_back();
1362*c8dee2aaSAndroid Build Coastguard Worker         return stackID;
1363*c8dee2aaSAndroid Build Coastguard Worker     }
1364*c8dee2aaSAndroid Build Coastguard Worker     return ++fNextStackID;
1365*c8dee2aaSAndroid Build Coastguard Worker }
1366*c8dee2aaSAndroid Build Coastguard Worker 
recycleStack(int stackID)1367*c8dee2aaSAndroid Build Coastguard Worker void Generator::recycleStack(int stackID) {
1368*c8dee2aaSAndroid Build Coastguard Worker     fRecycledStacks.push_back(stackID);
1369*c8dee2aaSAndroid Build Coastguard Worker }
1370*c8dee2aaSAndroid Build Coastguard Worker 
setCurrentStack(int stackID)1371*c8dee2aaSAndroid Build Coastguard Worker void Generator::setCurrentStack(int stackID) {
1372*c8dee2aaSAndroid Build Coastguard Worker     if (fCurrentStack != stackID) {
1373*c8dee2aaSAndroid Build Coastguard Worker         fCurrentStack = stackID;
1374*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.set_current_stack(stackID);
1375*c8dee2aaSAndroid Build Coastguard Worker     }
1376*c8dee2aaSAndroid Build Coastguard Worker }
1377*c8dee2aaSAndroid Build Coastguard Worker 
writeFunction(const IRNode & callSite,const FunctionDefinition & function,SkSpan<std::unique_ptr<Expression> const> arguments)1378*c8dee2aaSAndroid Build Coastguard Worker std::optional<SlotRange> Generator::writeFunction(
1379*c8dee2aaSAndroid Build Coastguard Worker         const IRNode& callSite,
1380*c8dee2aaSAndroid Build Coastguard Worker         const FunctionDefinition& function,
1381*c8dee2aaSAndroid Build Coastguard Worker         SkSpan<std::unique_ptr<Expression> const> arguments) {
1382*c8dee2aaSAndroid Build Coastguard Worker     // Generate debug information and emit a trace-enter op.
1383*c8dee2aaSAndroid Build Coastguard Worker     int funcIndex = -1;
1384*c8dee2aaSAndroid Build Coastguard Worker     if (fDebugTrace) {
1385*c8dee2aaSAndroid Build Coastguard Worker         funcIndex = this->getFunctionDebugInfo(function.declaration());
1386*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(funcIndex >= 0);
1387*c8dee2aaSAndroid Build Coastguard Worker         if (this->shouldWriteTraceOps()) {
1388*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.trace_enter(fTraceMask->stackID(), funcIndex);
1389*c8dee2aaSAndroid Build Coastguard Worker         }
1390*c8dee2aaSAndroid Build Coastguard Worker     }
1391*c8dee2aaSAndroid Build Coastguard Worker 
1392*c8dee2aaSAndroid Build Coastguard Worker     // Handle parameter lvalues.
1393*c8dee2aaSAndroid Build Coastguard Worker     struct RemappedSlotRange {
1394*c8dee2aaSAndroid Build Coastguard Worker         const Variable* fVariable;
1395*c8dee2aaSAndroid Build Coastguard Worker         std::optional<SlotRange> fSlotRange;
1396*c8dee2aaSAndroid Build Coastguard Worker     };
1397*c8dee2aaSAndroid Build Coastguard Worker     SkSpan<Variable* const> parameters = function.declaration().parameters();
1398*c8dee2aaSAndroid Build Coastguard Worker     TArray<std::unique_ptr<LValue>> lvalues;
1399*c8dee2aaSAndroid Build Coastguard Worker     TArray<RemappedSlotRange> remappedSlotRanges;
1400*c8dee2aaSAndroid Build Coastguard Worker 
1401*c8dee2aaSAndroid Build Coastguard Worker     if (function.declaration().isMain()) {
1402*c8dee2aaSAndroid Build Coastguard Worker         // For main(), the parameter slots have already been populated by `writeProgram`, but we
1403*c8dee2aaSAndroid Build Coastguard Worker         // still need to explicitly emit trace ops for the variables in main(), since they are
1404*c8dee2aaSAndroid Build Coastguard Worker         // initialized before it is safe to use trace-var. (We can't invoke init-lane-masks until
1405*c8dee2aaSAndroid Build Coastguard Worker         // after we've copied the inputs from main into slots, because dst.rgba is used to pass in a
1406*c8dee2aaSAndroid Build Coastguard Worker         // blend-destination color, but we clobber it and put in the execution mask instead.)
1407*c8dee2aaSAndroid Build Coastguard Worker         if (this->shouldWriteTraceOps()) {
1408*c8dee2aaSAndroid Build Coastguard Worker             for (const Variable* var : parameters) {
1409*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.trace_var(fTraceMask->stackID(), this->getVariableSlots(*var));
1410*c8dee2aaSAndroid Build Coastguard Worker             }
1411*c8dee2aaSAndroid Build Coastguard Worker         }
1412*c8dee2aaSAndroid Build Coastguard Worker     } else {
1413*c8dee2aaSAndroid Build Coastguard Worker         // Write all the arguments into their parameter's variable slots. Because we never allow
1414*c8dee2aaSAndroid Build Coastguard Worker         // recursion, we don't need to worry about overwriting any existing values in those slots.
1415*c8dee2aaSAndroid Build Coastguard Worker         // (In fact, we don't even need to apply the write mask.)
1416*c8dee2aaSAndroid Build Coastguard Worker         lvalues.resize(arguments.size());
1417*c8dee2aaSAndroid Build Coastguard Worker 
1418*c8dee2aaSAndroid Build Coastguard Worker         for (size_t index = 0; index < arguments.size(); ++index) {
1419*c8dee2aaSAndroid Build Coastguard Worker             const Expression& arg = *arguments[index];
1420*c8dee2aaSAndroid Build Coastguard Worker             const Variable& param = *parameters[index];
1421*c8dee2aaSAndroid Build Coastguard Worker 
1422*c8dee2aaSAndroid Build Coastguard Worker             // If we are passing a child effect to a function, we need to add its mapping to our
1423*c8dee2aaSAndroid Build Coastguard Worker             // child map.
1424*c8dee2aaSAndroid Build Coastguard Worker             if (arg.type().isEffectChild()) {
1425*c8dee2aaSAndroid Build Coastguard Worker                 if (int* childIndex = fChildEffectMap.find(arg.as<VariableReference>()
1426*c8dee2aaSAndroid Build Coastguard Worker                                                               .variable())) {
1427*c8dee2aaSAndroid Build Coastguard Worker                     SkASSERT(!fChildEffectMap.find(&param));
1428*c8dee2aaSAndroid Build Coastguard Worker                     fChildEffectMap[&param] = *childIndex;
1429*c8dee2aaSAndroid Build Coastguard Worker                 }
1430*c8dee2aaSAndroid Build Coastguard Worker                 continue;
1431*c8dee2aaSAndroid Build Coastguard Worker             }
1432*c8dee2aaSAndroid Build Coastguard Worker 
1433*c8dee2aaSAndroid Build Coastguard Worker             // Use LValues for out-parameters and inout-parameters, so we can store back to them
1434*c8dee2aaSAndroid Build Coastguard Worker             // later.
1435*c8dee2aaSAndroid Build Coastguard Worker             if (IsInoutParameter(param) || IsOutParameter(param)) {
1436*c8dee2aaSAndroid Build Coastguard Worker                 lvalues[index] = this->makeLValue(arg);
1437*c8dee2aaSAndroid Build Coastguard Worker                 if (!lvalues[index]) {
1438*c8dee2aaSAndroid Build Coastguard Worker                     return std::nullopt;
1439*c8dee2aaSAndroid Build Coastguard Worker                 }
1440*c8dee2aaSAndroid Build Coastguard Worker                 // There are no guarantees on the starting value of an out-parameter, so we only
1441*c8dee2aaSAndroid Build Coastguard Worker                 // need to store the lvalues associated with an inout parameter.
1442*c8dee2aaSAndroid Build Coastguard Worker                 if (IsInoutParameter(param)) {
1443*c8dee2aaSAndroid Build Coastguard Worker                     if (!this->push(*lvalues[index])) {
1444*c8dee2aaSAndroid Build Coastguard Worker                         return std::nullopt;
1445*c8dee2aaSAndroid Build Coastguard Worker                     }
1446*c8dee2aaSAndroid Build Coastguard Worker                     this->popToSlotRangeUnmasked(this->getVariableSlots(param));
1447*c8dee2aaSAndroid Build Coastguard Worker                 }
1448*c8dee2aaSAndroid Build Coastguard Worker                 continue;
1449*c8dee2aaSAndroid Build Coastguard Worker             }
1450*c8dee2aaSAndroid Build Coastguard Worker 
1451*c8dee2aaSAndroid Build Coastguard Worker             // If a parameter is never read by the function, we don't need to populate its slots.
1452*c8dee2aaSAndroid Build Coastguard Worker             ProgramUsage::VariableCounts paramCounts = fProgram.fUsage->get(param);
1453*c8dee2aaSAndroid Build Coastguard Worker             if (paramCounts.fRead == 0) {
1454*c8dee2aaSAndroid Build Coastguard Worker                 // Honor the expression's side effects, if any.
1455*c8dee2aaSAndroid Build Coastguard Worker                 if (Analysis::HasSideEffects(arg)) {
1456*c8dee2aaSAndroid Build Coastguard Worker                     if (!this->pushExpression(arg, /*usesResult=*/false)) {
1457*c8dee2aaSAndroid Build Coastguard Worker                         return std::nullopt;
1458*c8dee2aaSAndroid Build Coastguard Worker                     }
1459*c8dee2aaSAndroid Build Coastguard Worker                     this->discardExpression(arg.type().slotCount());
1460*c8dee2aaSAndroid Build Coastguard Worker                 }
1461*c8dee2aaSAndroid Build Coastguard Worker                 continue;
1462*c8dee2aaSAndroid Build Coastguard Worker             }
1463*c8dee2aaSAndroid Build Coastguard Worker 
1464*c8dee2aaSAndroid Build Coastguard Worker             // If the expression is a plain variable and the parameter is never written to, we don't
1465*c8dee2aaSAndroid Build Coastguard Worker             // need to copy it; we can just share the slots from the existing variable.
1466*c8dee2aaSAndroid Build Coastguard Worker             if (paramCounts.fWrite == 0 && arg.is<VariableReference>()) {
1467*c8dee2aaSAndroid Build Coastguard Worker                 const Variable& var = *arg.as<VariableReference>().variable();
1468*c8dee2aaSAndroid Build Coastguard Worker                 if (this->hasVariableSlots(var)) {
1469*c8dee2aaSAndroid Build Coastguard Worker                     std::optional<SlotRange> originalRange =
1470*c8dee2aaSAndroid Build Coastguard Worker                             fProgramSlots.mapVariableToSlots(param, this->getVariableSlots(var));
1471*c8dee2aaSAndroid Build Coastguard Worker                     remappedSlotRanges.push_back({&param, originalRange});
1472*c8dee2aaSAndroid Build Coastguard Worker                     continue;
1473*c8dee2aaSAndroid Build Coastguard Worker                 }
1474*c8dee2aaSAndroid Build Coastguard Worker             }
1475*c8dee2aaSAndroid Build Coastguard Worker 
1476*c8dee2aaSAndroid Build Coastguard Worker             // Copy input arguments into their respective parameter slots.
1477*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(arg)) {
1478*c8dee2aaSAndroid Build Coastguard Worker                 return std::nullopt;
1479*c8dee2aaSAndroid Build Coastguard Worker             }
1480*c8dee2aaSAndroid Build Coastguard Worker             this->popToSlotRangeUnmasked(this->getVariableSlots(param));
1481*c8dee2aaSAndroid Build Coastguard Worker         }
1482*c8dee2aaSAndroid Build Coastguard Worker     }
1483*c8dee2aaSAndroid Build Coastguard Worker 
1484*c8dee2aaSAndroid Build Coastguard Worker     // Set up a slot range dedicated to this function's return value.
1485*c8dee2aaSAndroid Build Coastguard Worker     SlotRange lastFunctionResult = fCurrentFunctionResult;
1486*c8dee2aaSAndroid Build Coastguard Worker     fCurrentFunctionResult = this->getFunctionSlots(callSite, function.declaration());
1487*c8dee2aaSAndroid Build Coastguard Worker 
1488*c8dee2aaSAndroid Build Coastguard Worker     // Save off the return mask.
1489*c8dee2aaSAndroid Build Coastguard Worker     if (this->needsReturnMask(&function)) {
1490*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.enableExecutionMaskWrites();
1491*c8dee2aaSAndroid Build Coastguard Worker         if (!function.declaration().isMain()) {
1492*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.push_return_mask();
1493*c8dee2aaSAndroid Build Coastguard Worker         }
1494*c8dee2aaSAndroid Build Coastguard Worker     }
1495*c8dee2aaSAndroid Build Coastguard Worker 
1496*c8dee2aaSAndroid Build Coastguard Worker     // Emit the function body.
1497*c8dee2aaSAndroid Build Coastguard Worker     if (!this->writeStatement(*function.body())) {
1498*c8dee2aaSAndroid Build Coastguard Worker         return std::nullopt;
1499*c8dee2aaSAndroid Build Coastguard Worker     }
1500*c8dee2aaSAndroid Build Coastguard Worker 
1501*c8dee2aaSAndroid Build Coastguard Worker     // Restore the original return mask.
1502*c8dee2aaSAndroid Build Coastguard Worker     if (this->needsReturnMask(&function)) {
1503*c8dee2aaSAndroid Build Coastguard Worker         if (!function.declaration().isMain()) {
1504*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.pop_return_mask();
1505*c8dee2aaSAndroid Build Coastguard Worker         }
1506*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.disableExecutionMaskWrites();
1507*c8dee2aaSAndroid Build Coastguard Worker     }
1508*c8dee2aaSAndroid Build Coastguard Worker 
1509*c8dee2aaSAndroid Build Coastguard Worker     // Restore the function-result slot range.
1510*c8dee2aaSAndroid Build Coastguard Worker     SlotRange functionResult = fCurrentFunctionResult;
1511*c8dee2aaSAndroid Build Coastguard Worker     fCurrentFunctionResult = lastFunctionResult;
1512*c8dee2aaSAndroid Build Coastguard Worker 
1513*c8dee2aaSAndroid Build Coastguard Worker     // Emit a trace-exit op.
1514*c8dee2aaSAndroid Build Coastguard Worker     if (fDebugTrace && fWriteTraceOps) {
1515*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.trace_exit(fTraceMask->stackID(), funcIndex);
1516*c8dee2aaSAndroid Build Coastguard Worker     }
1517*c8dee2aaSAndroid Build Coastguard Worker 
1518*c8dee2aaSAndroid Build Coastguard Worker     // Copy out-parameters and inout-parameters back to their homes.
1519*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index < lvalues.size(); ++index) {
1520*c8dee2aaSAndroid Build Coastguard Worker         if (lvalues[index]) {
1521*c8dee2aaSAndroid Build Coastguard Worker             // Only out- and inout-parameters should have an associated lvalue.
1522*c8dee2aaSAndroid Build Coastguard Worker             const Variable& param = *parameters[index];
1523*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(IsInoutParameter(param) || IsOutParameter(param));
1524*c8dee2aaSAndroid Build Coastguard Worker 
1525*c8dee2aaSAndroid Build Coastguard Worker             // Copy the parameter's slots directly into the lvalue.
1526*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.push_slots(this->getVariableSlots(param));
1527*c8dee2aaSAndroid Build Coastguard Worker             if (!this->store(*lvalues[index])) {
1528*c8dee2aaSAndroid Build Coastguard Worker                 return std::nullopt;
1529*c8dee2aaSAndroid Build Coastguard Worker             }
1530*c8dee2aaSAndroid Build Coastguard Worker             this->discardExpression(param.type().slotCount());
1531*c8dee2aaSAndroid Build Coastguard Worker         }
1532*c8dee2aaSAndroid Build Coastguard Worker     }
1533*c8dee2aaSAndroid Build Coastguard Worker 
1534*c8dee2aaSAndroid Build Coastguard Worker     // Restore any remapped parameter slot ranges to their original values.
1535*c8dee2aaSAndroid Build Coastguard Worker     for (const RemappedSlotRange& remapped : remappedSlotRanges) {
1536*c8dee2aaSAndroid Build Coastguard Worker         if (remapped.fSlotRange.has_value()) {
1537*c8dee2aaSAndroid Build Coastguard Worker             fProgramSlots.mapVariableToSlots(*remapped.fVariable, *remapped.fSlotRange);
1538*c8dee2aaSAndroid Build Coastguard Worker         } else {
1539*c8dee2aaSAndroid Build Coastguard Worker             fProgramSlots.unmapVariableSlots(*remapped.fVariable);
1540*c8dee2aaSAndroid Build Coastguard Worker         }
1541*c8dee2aaSAndroid Build Coastguard Worker     }
1542*c8dee2aaSAndroid Build Coastguard Worker 
1543*c8dee2aaSAndroid Build Coastguard Worker     // Remove any child-effect mappings that were made for this call.
1544*c8dee2aaSAndroid Build Coastguard Worker     for (size_t index = 0; index < arguments.size(); ++index) {
1545*c8dee2aaSAndroid Build Coastguard Worker         const Expression& arg = *arguments[index];
1546*c8dee2aaSAndroid Build Coastguard Worker         if (arg.type().isEffectChild()) {
1547*c8dee2aaSAndroid Build Coastguard Worker             fChildEffectMap.remove(parameters[index]);
1548*c8dee2aaSAndroid Build Coastguard Worker         }
1549*c8dee2aaSAndroid Build Coastguard Worker     }
1550*c8dee2aaSAndroid Build Coastguard Worker 
1551*c8dee2aaSAndroid Build Coastguard Worker     return functionResult;
1552*c8dee2aaSAndroid Build Coastguard Worker }
1553*c8dee2aaSAndroid Build Coastguard Worker 
emitTraceLine(Position pos)1554*c8dee2aaSAndroid Build Coastguard Worker void Generator::emitTraceLine(Position pos) {
1555*c8dee2aaSAndroid Build Coastguard Worker     if (fDebugTrace && fWriteTraceOps && pos.valid() && fInsideCompoundStatement == 0) {
1556*c8dee2aaSAndroid Build Coastguard Worker         // Binary search within fLineOffets to convert the position into a line number.
1557*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fLineOffsets.size() >= 2);
1558*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fLineOffsets[0] == 0);
1559*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fLineOffsets.back() == (int)fProgram.fSource->length());
1560*c8dee2aaSAndroid Build Coastguard Worker         int lineNumber = std::distance(
1561*c8dee2aaSAndroid Build Coastguard Worker                 fLineOffsets.begin(),
1562*c8dee2aaSAndroid Build Coastguard Worker                 std::upper_bound(fLineOffsets.begin(), fLineOffsets.end(), pos.startOffset()));
1563*c8dee2aaSAndroid Build Coastguard Worker 
1564*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.trace_line(fTraceMask->stackID(), lineNumber);
1565*c8dee2aaSAndroid Build Coastguard Worker     }
1566*c8dee2aaSAndroid Build Coastguard Worker }
1567*c8dee2aaSAndroid Build Coastguard Worker 
pushTraceScopeMask()1568*c8dee2aaSAndroid Build Coastguard Worker void Generator::pushTraceScopeMask() {
1569*c8dee2aaSAndroid Build Coastguard Worker     if (this->shouldWriteTraceOps()) {
1570*c8dee2aaSAndroid Build Coastguard Worker         // Take the intersection of the trace mask and the execution mask. To do this, start with an
1571*c8dee2aaSAndroid Build Coastguard Worker         // all-zero mask, then use select to overwrite those zeros with the trace mask across all
1572*c8dee2aaSAndroid Build Coastguard Worker         // executing lanes. We'll get the trace mask in executing lanes, and zero in dead lanes.
1573*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.push_constant_i(0);
1574*c8dee2aaSAndroid Build Coastguard Worker         fTraceMask->pushClone(/*slots=*/1);
1575*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.select(/*slots=*/1);
1576*c8dee2aaSAndroid Build Coastguard Worker     }
1577*c8dee2aaSAndroid Build Coastguard Worker }
1578*c8dee2aaSAndroid Build Coastguard Worker 
discardTraceScopeMask()1579*c8dee2aaSAndroid Build Coastguard Worker void Generator::discardTraceScopeMask() {
1580*c8dee2aaSAndroid Build Coastguard Worker     if (this->shouldWriteTraceOps()) {
1581*c8dee2aaSAndroid Build Coastguard Worker         this->discardExpression(/*slots=*/1);
1582*c8dee2aaSAndroid Build Coastguard Worker     }
1583*c8dee2aaSAndroid Build Coastguard Worker }
1584*c8dee2aaSAndroid Build Coastguard Worker 
emitTraceScope(int delta)1585*c8dee2aaSAndroid Build Coastguard Worker void Generator::emitTraceScope(int delta) {
1586*c8dee2aaSAndroid Build Coastguard Worker     if (this->shouldWriteTraceOps()) {
1587*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.trace_scope(this->currentStack(), delta);
1588*c8dee2aaSAndroid Build Coastguard Worker     }
1589*c8dee2aaSAndroid Build Coastguard Worker }
1590*c8dee2aaSAndroid Build Coastguard Worker 
calculateLineOffsets()1591*c8dee2aaSAndroid Build Coastguard Worker void Generator::calculateLineOffsets() {
1592*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fLineOffsets.empty());
1593*c8dee2aaSAndroid Build Coastguard Worker     fLineOffsets.push_back(0);
1594*c8dee2aaSAndroid Build Coastguard Worker     for (size_t i = 0; i < fProgram.fSource->length(); ++i) {
1595*c8dee2aaSAndroid Build Coastguard Worker         if ((*fProgram.fSource)[i] == '\n') {
1596*c8dee2aaSAndroid Build Coastguard Worker             fLineOffsets.push_back(i);
1597*c8dee2aaSAndroid Build Coastguard Worker         }
1598*c8dee2aaSAndroid Build Coastguard Worker     }
1599*c8dee2aaSAndroid Build Coastguard Worker     fLineOffsets.push_back(fProgram.fSource->length());
1600*c8dee2aaSAndroid Build Coastguard Worker }
1601*c8dee2aaSAndroid Build Coastguard Worker 
writeGlobals()1602*c8dee2aaSAndroid Build Coastguard Worker bool Generator::writeGlobals() {
1603*c8dee2aaSAndroid Build Coastguard Worker     for (const ProgramElement* e : fProgram.elements()) {
1604*c8dee2aaSAndroid Build Coastguard Worker         if (e->is<GlobalVarDeclaration>()) {
1605*c8dee2aaSAndroid Build Coastguard Worker             const GlobalVarDeclaration& gvd = e->as<GlobalVarDeclaration>();
1606*c8dee2aaSAndroid Build Coastguard Worker             const VarDeclaration& decl = gvd.varDeclaration();
1607*c8dee2aaSAndroid Build Coastguard Worker             const Variable* var = decl.var();
1608*c8dee2aaSAndroid Build Coastguard Worker 
1609*c8dee2aaSAndroid Build Coastguard Worker             if (var->type().isEffectChild()) {
1610*c8dee2aaSAndroid Build Coastguard Worker                 // Associate each child effect variable with its numeric index.
1611*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(!fChildEffectMap.find(var));
1612*c8dee2aaSAndroid Build Coastguard Worker                 int childEffectIndex = fChildEffectMap.count();
1613*c8dee2aaSAndroid Build Coastguard Worker                 fChildEffectMap[var] = childEffectIndex;
1614*c8dee2aaSAndroid Build Coastguard Worker                 continue;
1615*c8dee2aaSAndroid Build Coastguard Worker             }
1616*c8dee2aaSAndroid Build Coastguard Worker 
1617*c8dee2aaSAndroid Build Coastguard Worker             // Opaque types include child processors and GL objects (samplers, textures, etc).
1618*c8dee2aaSAndroid Build Coastguard Worker             // Of those, only child processors are legal variables.
1619*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(!var->type().isVoid());
1620*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(!var->type().isOpaque());
1621*c8dee2aaSAndroid Build Coastguard Worker 
1622*c8dee2aaSAndroid Build Coastguard Worker             // Builtin variables are system-defined, with special semantics.
1623*c8dee2aaSAndroid Build Coastguard Worker             if (int builtin = var->layout().fBuiltin; builtin >= 0) {
1624*c8dee2aaSAndroid Build Coastguard Worker                 if (builtin == SK_FRAGCOORD_BUILTIN) {
1625*c8dee2aaSAndroid Build Coastguard Worker                     fBuilder.store_device_xy01(this->getVariableSlots(*var));
1626*c8dee2aaSAndroid Build Coastguard Worker                     continue;
1627*c8dee2aaSAndroid Build Coastguard Worker                 }
1628*c8dee2aaSAndroid Build Coastguard Worker                 // The only builtin variable exposed to runtime effects is sk_FragCoord.
1629*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
1630*c8dee2aaSAndroid Build Coastguard Worker             }
1631*c8dee2aaSAndroid Build Coastguard Worker 
1632*c8dee2aaSAndroid Build Coastguard Worker             if (IsUniform(*var)) {
1633*c8dee2aaSAndroid Build Coastguard Worker                 // Create the uniform slot map in first-to-last order.
1634*c8dee2aaSAndroid Build Coastguard Worker                 SlotRange uniformSlotRange = this->getUniformSlots(*var);
1635*c8dee2aaSAndroid Build Coastguard Worker 
1636*c8dee2aaSAndroid Build Coastguard Worker                 if (this->shouldWriteTraceOps()) {
1637*c8dee2aaSAndroid Build Coastguard Worker                     // We expect uniform values to show up in the debug trace. To make this happen
1638*c8dee2aaSAndroid Build Coastguard Worker                     // without updating the file format, we synthesize a value-slot range for the
1639*c8dee2aaSAndroid Build Coastguard Worker                     // uniform here, and copy the uniform data into the value slots. This allows
1640*c8dee2aaSAndroid Build Coastguard Worker                     // trace_var to work naturally. This wastes a bit of memory, but debug traces
1641*c8dee2aaSAndroid Build Coastguard Worker                     // don't need to be hyper-efficient.
1642*c8dee2aaSAndroid Build Coastguard Worker                     SlotRange copyRange = fProgramSlots.getVariableSlots(*var);
1643*c8dee2aaSAndroid Build Coastguard Worker                     fBuilder.push_uniform(uniformSlotRange);
1644*c8dee2aaSAndroid Build Coastguard Worker                     this->popToSlotRangeUnmasked(copyRange);
1645*c8dee2aaSAndroid Build Coastguard Worker                 }
1646*c8dee2aaSAndroid Build Coastguard Worker 
1647*c8dee2aaSAndroid Build Coastguard Worker                 continue;
1648*c8dee2aaSAndroid Build Coastguard Worker             }
1649*c8dee2aaSAndroid Build Coastguard Worker 
1650*c8dee2aaSAndroid Build Coastguard Worker             // Other globals are treated as normal variable declarations.
1651*c8dee2aaSAndroid Build Coastguard Worker             if (!this->writeVarDeclaration(decl)) {
1652*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
1653*c8dee2aaSAndroid Build Coastguard Worker             }
1654*c8dee2aaSAndroid Build Coastguard Worker         }
1655*c8dee2aaSAndroid Build Coastguard Worker     }
1656*c8dee2aaSAndroid Build Coastguard Worker 
1657*c8dee2aaSAndroid Build Coastguard Worker     return true;
1658*c8dee2aaSAndroid Build Coastguard Worker }
1659*c8dee2aaSAndroid Build Coastguard Worker 
writeStatement(const Statement & s)1660*c8dee2aaSAndroid Build Coastguard Worker bool Generator::writeStatement(const Statement& s) {
1661*c8dee2aaSAndroid Build Coastguard Worker     switch (s.kind()) {
1662*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kBlock:
1663*c8dee2aaSAndroid Build Coastguard Worker             // The debugger will stop on statements inside Blocks; there's no need for an additional
1664*c8dee2aaSAndroid Build Coastguard Worker             // stop on the block's initial open-brace.
1665*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kFor:
1666*c8dee2aaSAndroid Build Coastguard Worker             // The debugger will stop on the init-statement of a for statement, so we don't need to
1667*c8dee2aaSAndroid Build Coastguard Worker             // stop on the outer for-statement itself as well.
1668*c8dee2aaSAndroid Build Coastguard Worker             break;
1669*c8dee2aaSAndroid Build Coastguard Worker 
1670*c8dee2aaSAndroid Build Coastguard Worker         default:
1671*c8dee2aaSAndroid Build Coastguard Worker             // The debugger should stop on other statements.
1672*c8dee2aaSAndroid Build Coastguard Worker             this->emitTraceLine(s.fPosition);
1673*c8dee2aaSAndroid Build Coastguard Worker             break;
1674*c8dee2aaSAndroid Build Coastguard Worker     }
1675*c8dee2aaSAndroid Build Coastguard Worker 
1676*c8dee2aaSAndroid Build Coastguard Worker     switch (s.kind()) {
1677*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kBlock:
1678*c8dee2aaSAndroid Build Coastguard Worker             return this->writeBlock(s.as<Block>());
1679*c8dee2aaSAndroid Build Coastguard Worker 
1680*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kBreak:
1681*c8dee2aaSAndroid Build Coastguard Worker             return this->writeBreakStatement(s.as<BreakStatement>());
1682*c8dee2aaSAndroid Build Coastguard Worker 
1683*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kContinue:
1684*c8dee2aaSAndroid Build Coastguard Worker             return this->writeContinueStatement(s.as<ContinueStatement>());
1685*c8dee2aaSAndroid Build Coastguard Worker 
1686*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kDo:
1687*c8dee2aaSAndroid Build Coastguard Worker             return this->writeDoStatement(s.as<DoStatement>());
1688*c8dee2aaSAndroid Build Coastguard Worker 
1689*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kExpression:
1690*c8dee2aaSAndroid Build Coastguard Worker             return this->writeExpressionStatement(s.as<ExpressionStatement>());
1691*c8dee2aaSAndroid Build Coastguard Worker 
1692*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kFor:
1693*c8dee2aaSAndroid Build Coastguard Worker             return this->writeForStatement(s.as<ForStatement>());
1694*c8dee2aaSAndroid Build Coastguard Worker 
1695*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kIf:
1696*c8dee2aaSAndroid Build Coastguard Worker             return this->writeIfStatement(s.as<IfStatement>());
1697*c8dee2aaSAndroid Build Coastguard Worker 
1698*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kNop:
1699*c8dee2aaSAndroid Build Coastguard Worker             return true;
1700*c8dee2aaSAndroid Build Coastguard Worker 
1701*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kReturn:
1702*c8dee2aaSAndroid Build Coastguard Worker             return this->writeReturnStatement(s.as<ReturnStatement>());
1703*c8dee2aaSAndroid Build Coastguard Worker 
1704*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kSwitch:
1705*c8dee2aaSAndroid Build Coastguard Worker             return this->writeSwitchStatement(s.as<SwitchStatement>());
1706*c8dee2aaSAndroid Build Coastguard Worker 
1707*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kVarDeclaration:
1708*c8dee2aaSAndroid Build Coastguard Worker             return this->writeVarDeclaration(s.as<VarDeclaration>());
1709*c8dee2aaSAndroid Build Coastguard Worker 
1710*c8dee2aaSAndroid Build Coastguard Worker         default:
1711*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
1712*c8dee2aaSAndroid Build Coastguard Worker     }
1713*c8dee2aaSAndroid Build Coastguard Worker }
1714*c8dee2aaSAndroid Build Coastguard Worker 
writeBlock(const Block & b)1715*c8dee2aaSAndroid Build Coastguard Worker bool Generator::writeBlock(const Block& b) {
1716*c8dee2aaSAndroid Build Coastguard Worker     if (b.blockKind() == Block::Kind::kCompoundStatement) {
1717*c8dee2aaSAndroid Build Coastguard Worker         this->emitTraceLine(b.fPosition);
1718*c8dee2aaSAndroid Build Coastguard Worker         ++fInsideCompoundStatement;
1719*c8dee2aaSAndroid Build Coastguard Worker     } else {
1720*c8dee2aaSAndroid Build Coastguard Worker         this->pushTraceScopeMask();
1721*c8dee2aaSAndroid Build Coastguard Worker         this->emitTraceScope(+1);
1722*c8dee2aaSAndroid Build Coastguard Worker     }
1723*c8dee2aaSAndroid Build Coastguard Worker 
1724*c8dee2aaSAndroid Build Coastguard Worker     for (const std::unique_ptr<Statement>& stmt : b.children()) {
1725*c8dee2aaSAndroid Build Coastguard Worker         if (!this->writeStatement(*stmt)) {
1726*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
1727*c8dee2aaSAndroid Build Coastguard Worker         }
1728*c8dee2aaSAndroid Build Coastguard Worker     }
1729*c8dee2aaSAndroid Build Coastguard Worker 
1730*c8dee2aaSAndroid Build Coastguard Worker     if (b.blockKind() == Block::Kind::kCompoundStatement) {
1731*c8dee2aaSAndroid Build Coastguard Worker         --fInsideCompoundStatement;
1732*c8dee2aaSAndroid Build Coastguard Worker     } else {
1733*c8dee2aaSAndroid Build Coastguard Worker         this->emitTraceScope(-1);
1734*c8dee2aaSAndroid Build Coastguard Worker         this->discardTraceScopeMask();
1735*c8dee2aaSAndroid Build Coastguard Worker     }
1736*c8dee2aaSAndroid Build Coastguard Worker 
1737*c8dee2aaSAndroid Build Coastguard Worker     return true;
1738*c8dee2aaSAndroid Build Coastguard Worker }
1739*c8dee2aaSAndroid Build Coastguard Worker 
writeBreakStatement(const BreakStatement &)1740*c8dee2aaSAndroid Build Coastguard Worker bool Generator::writeBreakStatement(const BreakStatement&) {
1741*c8dee2aaSAndroid Build Coastguard Worker     // If all lanes have reached this break, we can just branch straight to the break target instead
1742*c8dee2aaSAndroid Build Coastguard Worker     // of updating masks.
1743*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.branch_if_all_lanes_active(fCurrentBreakTarget);
1744*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.mask_off_loop_mask();
1745*c8dee2aaSAndroid Build Coastguard Worker     return true;
1746*c8dee2aaSAndroid Build Coastguard Worker }
1747*c8dee2aaSAndroid Build Coastguard Worker 
writeContinueStatement(const ContinueStatement &)1748*c8dee2aaSAndroid Build Coastguard Worker bool Generator::writeContinueStatement(const ContinueStatement&) {
1749*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.continue_op(fCurrentContinueMask->stackID());
1750*c8dee2aaSAndroid Build Coastguard Worker     return true;
1751*c8dee2aaSAndroid Build Coastguard Worker }
1752*c8dee2aaSAndroid Build Coastguard Worker 
writeDoStatement(const DoStatement & d)1753*c8dee2aaSAndroid Build Coastguard Worker bool Generator::writeDoStatement(const DoStatement& d) {
1754*c8dee2aaSAndroid Build Coastguard Worker     // Set up a break target.
1755*c8dee2aaSAndroid Build Coastguard Worker     AutoLoopTarget breakTarget(this, &fCurrentBreakTarget);
1756*c8dee2aaSAndroid Build Coastguard Worker 
1757*c8dee2aaSAndroid Build Coastguard Worker     // Save off the original loop mask.
1758*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.enableExecutionMaskWrites();
1759*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.push_loop_mask();
1760*c8dee2aaSAndroid Build Coastguard Worker 
1761*c8dee2aaSAndroid Build Coastguard Worker     // If `continue` is used in the loop...
1762*c8dee2aaSAndroid Build Coastguard Worker     Analysis::LoopControlFlowInfo loopInfo = Analysis::GetLoopControlFlowInfo(*d.statement());
1763*c8dee2aaSAndroid Build Coastguard Worker     AutoContinueMask autoContinueMask(this);
1764*c8dee2aaSAndroid Build Coastguard Worker     if (loopInfo.fHasContinue) {
1765*c8dee2aaSAndroid Build Coastguard Worker         // ... create a temporary slot for continue-mask storage.
1766*c8dee2aaSAndroid Build Coastguard Worker         autoContinueMask.enable();
1767*c8dee2aaSAndroid Build Coastguard Worker     }
1768*c8dee2aaSAndroid Build Coastguard Worker 
1769*c8dee2aaSAndroid Build Coastguard Worker     // Write the do-loop body.
1770*c8dee2aaSAndroid Build Coastguard Worker     int labelID = fBuilder.nextLabelID();
1771*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.label(labelID);
1772*c8dee2aaSAndroid Build Coastguard Worker 
1773*c8dee2aaSAndroid Build Coastguard Worker     autoContinueMask.enterLoopBody();
1774*c8dee2aaSAndroid Build Coastguard Worker 
1775*c8dee2aaSAndroid Build Coastguard Worker     if (!this->writeStatement(*d.statement())) {
1776*c8dee2aaSAndroid Build Coastguard Worker         return false;
1777*c8dee2aaSAndroid Build Coastguard Worker     }
1778*c8dee2aaSAndroid Build Coastguard Worker 
1779*c8dee2aaSAndroid Build Coastguard Worker     autoContinueMask.exitLoopBody();
1780*c8dee2aaSAndroid Build Coastguard Worker 
1781*c8dee2aaSAndroid Build Coastguard Worker     // Point the debugger at the do-statement's test-expression before we run it.
1782*c8dee2aaSAndroid Build Coastguard Worker     this->emitTraceLine(d.test()->fPosition);
1783*c8dee2aaSAndroid Build Coastguard Worker 
1784*c8dee2aaSAndroid Build Coastguard Worker     // Emit the test-expression, in order to combine it with the loop mask.
1785*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushExpression(*d.test())) {
1786*c8dee2aaSAndroid Build Coastguard Worker         return false;
1787*c8dee2aaSAndroid Build Coastguard Worker     }
1788*c8dee2aaSAndroid Build Coastguard Worker 
1789*c8dee2aaSAndroid Build Coastguard Worker     // Mask off any lanes in the loop mask where the test-expression is false; this breaks the loop.
1790*c8dee2aaSAndroid Build Coastguard Worker     // We don't use the test expression for anything else, so jettison it.
1791*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.merge_loop_mask();
1792*c8dee2aaSAndroid Build Coastguard Worker     this->discardExpression(/*slots=*/1);
1793*c8dee2aaSAndroid Build Coastguard Worker 
1794*c8dee2aaSAndroid Build Coastguard Worker     // If any lanes are still running, go back to the top and run the loop body again.
1795*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.branch_if_any_lanes_active(labelID);
1796*c8dee2aaSAndroid Build Coastguard Worker 
1797*c8dee2aaSAndroid Build Coastguard Worker     // If we hit a break statement on all lanes, we will branch here to escape from the loop.
1798*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.label(breakTarget.labelID());
1799*c8dee2aaSAndroid Build Coastguard Worker 
1800*c8dee2aaSAndroid Build Coastguard Worker     // Restore the loop mask.
1801*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.pop_loop_mask();
1802*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.disableExecutionMaskWrites();
1803*c8dee2aaSAndroid Build Coastguard Worker 
1804*c8dee2aaSAndroid Build Coastguard Worker     return true;
1805*c8dee2aaSAndroid Build Coastguard Worker }
1806*c8dee2aaSAndroid Build Coastguard Worker 
writeMasklessForStatement(const ForStatement & f)1807*c8dee2aaSAndroid Build Coastguard Worker bool Generator::writeMasklessForStatement(const ForStatement& f) {
1808*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(f.unrollInfo());
1809*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(f.unrollInfo()->fCount > 0);
1810*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(f.initializer());
1811*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(f.test());
1812*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(f.next());
1813*c8dee2aaSAndroid Build Coastguard Worker 
1814*c8dee2aaSAndroid Build Coastguard Worker     // We want the loop index to disappear at the end of the loop, so wrap the for statement in a
1815*c8dee2aaSAndroid Build Coastguard Worker     // trace scope.
1816*c8dee2aaSAndroid Build Coastguard Worker     this->pushTraceScopeMask();
1817*c8dee2aaSAndroid Build Coastguard Worker     this->emitTraceScope(+1);
1818*c8dee2aaSAndroid Build Coastguard Worker 
1819*c8dee2aaSAndroid Build Coastguard Worker     // If no lanes are active, skip over the loop entirely. This guards against looping forever;
1820*c8dee2aaSAndroid Build Coastguard Worker     // with no lanes active, we wouldn't be able to write the loop variable back to its slot, so
1821*c8dee2aaSAndroid Build Coastguard Worker     // we'd never make forward progress.
1822*c8dee2aaSAndroid Build Coastguard Worker     int loopExitID = fBuilder.nextLabelID();
1823*c8dee2aaSAndroid Build Coastguard Worker     int loopBodyID = fBuilder.nextLabelID();
1824*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.branch_if_no_lanes_active(loopExitID);
1825*c8dee2aaSAndroid Build Coastguard Worker 
1826*c8dee2aaSAndroid Build Coastguard Worker     // Run the loop initializer.
1827*c8dee2aaSAndroid Build Coastguard Worker     if (!this->writeStatement(*f.initializer())) {
1828*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
1829*c8dee2aaSAndroid Build Coastguard Worker     }
1830*c8dee2aaSAndroid Build Coastguard Worker 
1831*c8dee2aaSAndroid Build Coastguard Worker     // Write the for-loop body. We know the for-loop has a standard ES2 unrollable structure, and
1832*c8dee2aaSAndroid Build Coastguard Worker     // that it runs for at least one iteration, so we can plow straight ahead into the loop body
1833*c8dee2aaSAndroid Build Coastguard Worker     // instead of running the loop-test first.
1834*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.label(loopBodyID);
1835*c8dee2aaSAndroid Build Coastguard Worker 
1836*c8dee2aaSAndroid Build Coastguard Worker     if (!this->writeStatement(*f.statement())) {
1837*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
1838*c8dee2aaSAndroid Build Coastguard Worker     }
1839*c8dee2aaSAndroid Build Coastguard Worker 
1840*c8dee2aaSAndroid Build Coastguard Worker     // Point the debugger at the for-statement's next-expression before we run it, or as close as we
1841*c8dee2aaSAndroid Build Coastguard Worker     // can reasonably get.
1842*c8dee2aaSAndroid Build Coastguard Worker     if (f.next()) {
1843*c8dee2aaSAndroid Build Coastguard Worker         this->emitTraceLine(f.next()->fPosition);
1844*c8dee2aaSAndroid Build Coastguard Worker     } else if (f.test()) {
1845*c8dee2aaSAndroid Build Coastguard Worker         this->emitTraceLine(f.test()->fPosition);
1846*c8dee2aaSAndroid Build Coastguard Worker     } else {
1847*c8dee2aaSAndroid Build Coastguard Worker         this->emitTraceLine(f.fPosition);
1848*c8dee2aaSAndroid Build Coastguard Worker     }
1849*c8dee2aaSAndroid Build Coastguard Worker 
1850*c8dee2aaSAndroid Build Coastguard Worker     // If the loop only runs for a single iteration, we are already done. If not...
1851*c8dee2aaSAndroid Build Coastguard Worker     if (f.unrollInfo()->fCount > 1) {
1852*c8dee2aaSAndroid Build Coastguard Worker         // ... run the next-expression, and immediately discard its result.
1853*c8dee2aaSAndroid Build Coastguard Worker         if (!this->pushExpression(*f.next(), /*usesResult=*/false)) {
1854*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
1855*c8dee2aaSAndroid Build Coastguard Worker         }
1856*c8dee2aaSAndroid Build Coastguard Worker         this->discardExpression(f.next()->type().slotCount());
1857*c8dee2aaSAndroid Build Coastguard Worker 
1858*c8dee2aaSAndroid Build Coastguard Worker         // Run the test-expression, and repeat the loop until the test-expression evaluates false.
1859*c8dee2aaSAndroid Build Coastguard Worker         if (!this->pushExpression(*f.test())) {
1860*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
1861*c8dee2aaSAndroid Build Coastguard Worker         }
1862*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.branch_if_no_active_lanes_on_stack_top_equal(0, loopBodyID);
1863*c8dee2aaSAndroid Build Coastguard Worker 
1864*c8dee2aaSAndroid Build Coastguard Worker         // Jettison the test-expression.
1865*c8dee2aaSAndroid Build Coastguard Worker         this->discardExpression(/*slots=*/1);
1866*c8dee2aaSAndroid Build Coastguard Worker     }
1867*c8dee2aaSAndroid Build Coastguard Worker 
1868*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.label(loopExitID);
1869*c8dee2aaSAndroid Build Coastguard Worker 
1870*c8dee2aaSAndroid Build Coastguard Worker     this->emitTraceScope(-1);
1871*c8dee2aaSAndroid Build Coastguard Worker     this->discardTraceScopeMask();
1872*c8dee2aaSAndroid Build Coastguard Worker     return true;
1873*c8dee2aaSAndroid Build Coastguard Worker }
1874*c8dee2aaSAndroid Build Coastguard Worker 
writeForStatement(const ForStatement & f)1875*c8dee2aaSAndroid Build Coastguard Worker bool Generator::writeForStatement(const ForStatement& f) {
1876*c8dee2aaSAndroid Build Coastguard Worker     // If we've determined that the loop does not run, omit its code entirely.
1877*c8dee2aaSAndroid Build Coastguard Worker     if (f.unrollInfo() && f.unrollInfo()->fCount == 0) {
1878*c8dee2aaSAndroid Build Coastguard Worker         return true;
1879*c8dee2aaSAndroid Build Coastguard Worker     }
1880*c8dee2aaSAndroid Build Coastguard Worker 
1881*c8dee2aaSAndroid Build Coastguard Worker     // If the loop doesn't escape early due to a `continue`, `break` or `return`, and the loop
1882*c8dee2aaSAndroid Build Coastguard Worker     // conforms to ES2 structure, we know that we will run the full number of iterations across all
1883*c8dee2aaSAndroid Build Coastguard Worker     // lanes and don't need to use a loop mask.
1884*c8dee2aaSAndroid Build Coastguard Worker     Analysis::LoopControlFlowInfo loopInfo = Analysis::GetLoopControlFlowInfo(*f.statement());
1885*c8dee2aaSAndroid Build Coastguard Worker     if (!loopInfo.fHasContinue && !loopInfo.fHasBreak && !loopInfo.fHasReturn && f.unrollInfo()) {
1886*c8dee2aaSAndroid Build Coastguard Worker         return this->writeMasklessForStatement(f);
1887*c8dee2aaSAndroid Build Coastguard Worker     }
1888*c8dee2aaSAndroid Build Coastguard Worker 
1889*c8dee2aaSAndroid Build Coastguard Worker     // We want the loop index to disappear at the end of the loop, so wrap the for statement in a
1890*c8dee2aaSAndroid Build Coastguard Worker     // trace scope.
1891*c8dee2aaSAndroid Build Coastguard Worker     this->pushTraceScopeMask();
1892*c8dee2aaSAndroid Build Coastguard Worker     this->emitTraceScope(+1);
1893*c8dee2aaSAndroid Build Coastguard Worker 
1894*c8dee2aaSAndroid Build Coastguard Worker     // Set up a break target.
1895*c8dee2aaSAndroid Build Coastguard Worker     AutoLoopTarget breakTarget(this, &fCurrentBreakTarget);
1896*c8dee2aaSAndroid Build Coastguard Worker 
1897*c8dee2aaSAndroid Build Coastguard Worker     // Run the loop initializer.
1898*c8dee2aaSAndroid Build Coastguard Worker     if (f.initializer()) {
1899*c8dee2aaSAndroid Build Coastguard Worker         if (!this->writeStatement(*f.initializer())) {
1900*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
1901*c8dee2aaSAndroid Build Coastguard Worker         }
1902*c8dee2aaSAndroid Build Coastguard Worker     } else {
1903*c8dee2aaSAndroid Build Coastguard Worker         this->emitTraceLine(f.fPosition);
1904*c8dee2aaSAndroid Build Coastguard Worker     }
1905*c8dee2aaSAndroid Build Coastguard Worker 
1906*c8dee2aaSAndroid Build Coastguard Worker     AutoContinueMask autoContinueMask(this);
1907*c8dee2aaSAndroid Build Coastguard Worker     if (loopInfo.fHasContinue) {
1908*c8dee2aaSAndroid Build Coastguard Worker         // Acquire a temporary slot for continue-mask storage.
1909*c8dee2aaSAndroid Build Coastguard Worker         autoContinueMask.enable();
1910*c8dee2aaSAndroid Build Coastguard Worker     }
1911*c8dee2aaSAndroid Build Coastguard Worker 
1912*c8dee2aaSAndroid Build Coastguard Worker     // Save off the original loop mask.
1913*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.enableExecutionMaskWrites();
1914*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.push_loop_mask();
1915*c8dee2aaSAndroid Build Coastguard Worker 
1916*c8dee2aaSAndroid Build Coastguard Worker     int loopTestID = fBuilder.nextLabelID();
1917*c8dee2aaSAndroid Build Coastguard Worker     int loopBodyID = fBuilder.nextLabelID();
1918*c8dee2aaSAndroid Build Coastguard Worker 
1919*c8dee2aaSAndroid Build Coastguard Worker     // Jump down to the loop test so we can fall out of the loop immediately if it's zero-iteration.
1920*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.jump(loopTestID);
1921*c8dee2aaSAndroid Build Coastguard Worker 
1922*c8dee2aaSAndroid Build Coastguard Worker     // Write the for-loop body.
1923*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.label(loopBodyID);
1924*c8dee2aaSAndroid Build Coastguard Worker 
1925*c8dee2aaSAndroid Build Coastguard Worker     autoContinueMask.enterLoopBody();
1926*c8dee2aaSAndroid Build Coastguard Worker 
1927*c8dee2aaSAndroid Build Coastguard Worker     if (!this->writeStatement(*f.statement())) {
1928*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
1929*c8dee2aaSAndroid Build Coastguard Worker     }
1930*c8dee2aaSAndroid Build Coastguard Worker 
1931*c8dee2aaSAndroid Build Coastguard Worker     autoContinueMask.exitLoopBody();
1932*c8dee2aaSAndroid Build Coastguard Worker 
1933*c8dee2aaSAndroid Build Coastguard Worker     // Point the debugger at the for-statement's next-expression before we run it, or as close as we
1934*c8dee2aaSAndroid Build Coastguard Worker     // can reasonably get.
1935*c8dee2aaSAndroid Build Coastguard Worker     if (f.next()) {
1936*c8dee2aaSAndroid Build Coastguard Worker         this->emitTraceLine(f.next()->fPosition);
1937*c8dee2aaSAndroid Build Coastguard Worker     } else if (f.test()) {
1938*c8dee2aaSAndroid Build Coastguard Worker         this->emitTraceLine(f.test()->fPosition);
1939*c8dee2aaSAndroid Build Coastguard Worker     } else {
1940*c8dee2aaSAndroid Build Coastguard Worker         this->emitTraceLine(f.fPosition);
1941*c8dee2aaSAndroid Build Coastguard Worker     }
1942*c8dee2aaSAndroid Build Coastguard Worker 
1943*c8dee2aaSAndroid Build Coastguard Worker     // Run the next-expression. Immediately discard its result.
1944*c8dee2aaSAndroid Build Coastguard Worker     if (f.next()) {
1945*c8dee2aaSAndroid Build Coastguard Worker         if (!this->pushExpression(*f.next(), /*usesResult=*/false)) {
1946*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
1947*c8dee2aaSAndroid Build Coastguard Worker         }
1948*c8dee2aaSAndroid Build Coastguard Worker         this->discardExpression(f.next()->type().slotCount());
1949*c8dee2aaSAndroid Build Coastguard Worker     }
1950*c8dee2aaSAndroid Build Coastguard Worker 
1951*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.label(loopTestID);
1952*c8dee2aaSAndroid Build Coastguard Worker     if (f.test()) {
1953*c8dee2aaSAndroid Build Coastguard Worker         // Emit the test-expression, in order to combine it with the loop mask.
1954*c8dee2aaSAndroid Build Coastguard Worker         if (!this->pushExpression(*f.test())) {
1955*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
1956*c8dee2aaSAndroid Build Coastguard Worker         }
1957*c8dee2aaSAndroid Build Coastguard Worker         // Mask off any lanes in the loop mask where the test-expression is false; this breaks the
1958*c8dee2aaSAndroid Build Coastguard Worker         // loop. We don't use the test expression for anything else, so jettison it.
1959*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.merge_loop_mask();
1960*c8dee2aaSAndroid Build Coastguard Worker         this->discardExpression(/*slots=*/1);
1961*c8dee2aaSAndroid Build Coastguard Worker     }
1962*c8dee2aaSAndroid Build Coastguard Worker 
1963*c8dee2aaSAndroid Build Coastguard Worker     // If any lanes are still running, go back to the top and run the loop body again.
1964*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.branch_if_any_lanes_active(loopBodyID);
1965*c8dee2aaSAndroid Build Coastguard Worker 
1966*c8dee2aaSAndroid Build Coastguard Worker     // If we hit a break statement on all lanes, we will branch here to escape from the loop.
1967*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.label(breakTarget.labelID());
1968*c8dee2aaSAndroid Build Coastguard Worker 
1969*c8dee2aaSAndroid Build Coastguard Worker     // Restore the loop mask.
1970*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.pop_loop_mask();
1971*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.disableExecutionMaskWrites();
1972*c8dee2aaSAndroid Build Coastguard Worker 
1973*c8dee2aaSAndroid Build Coastguard Worker     this->emitTraceScope(-1);
1974*c8dee2aaSAndroid Build Coastguard Worker     this->discardTraceScopeMask();
1975*c8dee2aaSAndroid Build Coastguard Worker     return true;
1976*c8dee2aaSAndroid Build Coastguard Worker }
1977*c8dee2aaSAndroid Build Coastguard Worker 
writeExpressionStatement(const ExpressionStatement & e)1978*c8dee2aaSAndroid Build Coastguard Worker bool Generator::writeExpressionStatement(const ExpressionStatement& e) {
1979*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushExpression(*e.expression(), /*usesResult=*/false)) {
1980*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
1981*c8dee2aaSAndroid Build Coastguard Worker     }
1982*c8dee2aaSAndroid Build Coastguard Worker     this->discardExpression(e.expression()->type().slotCount());
1983*c8dee2aaSAndroid Build Coastguard Worker     return true;
1984*c8dee2aaSAndroid Build Coastguard Worker }
1985*c8dee2aaSAndroid Build Coastguard Worker 
writeDynamicallyUniformIfStatement(const IfStatement & i)1986*c8dee2aaSAndroid Build Coastguard Worker bool Generator::writeDynamicallyUniformIfStatement(const IfStatement& i) {
1987*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(Analysis::IsDynamicallyUniformExpression(*i.test()));
1988*c8dee2aaSAndroid Build Coastguard Worker 
1989*c8dee2aaSAndroid Build Coastguard Worker     int falseLabelID = fBuilder.nextLabelID();
1990*c8dee2aaSAndroid Build Coastguard Worker     int exitLabelID = fBuilder.nextLabelID();
1991*c8dee2aaSAndroid Build Coastguard Worker 
1992*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushExpression(*i.test())) {
1993*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
1994*c8dee2aaSAndroid Build Coastguard Worker     }
1995*c8dee2aaSAndroid Build Coastguard Worker 
1996*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.branch_if_no_active_lanes_on_stack_top_equal(~0, falseLabelID);
1997*c8dee2aaSAndroid Build Coastguard Worker 
1998*c8dee2aaSAndroid Build Coastguard Worker     if (!this->writeStatement(*i.ifTrue())) {
1999*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
2000*c8dee2aaSAndroid Build Coastguard Worker     }
2001*c8dee2aaSAndroid Build Coastguard Worker 
2002*c8dee2aaSAndroid Build Coastguard Worker     if (!i.ifFalse()) {
2003*c8dee2aaSAndroid Build Coastguard Worker         // We don't have an if-false condition at all.
2004*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.label(falseLabelID);
2005*c8dee2aaSAndroid Build Coastguard Worker     } else {
2006*c8dee2aaSAndroid Build Coastguard Worker         // We do have an if-false condition. We've just completed the if-true block, so we need to
2007*c8dee2aaSAndroid Build Coastguard Worker         // jump past the if-false block to avoid executing it.
2008*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.jump(exitLabelID);
2009*c8dee2aaSAndroid Build Coastguard Worker 
2010*c8dee2aaSAndroid Build Coastguard Worker         // The if-false block starts here.
2011*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.label(falseLabelID);
2012*c8dee2aaSAndroid Build Coastguard Worker 
2013*c8dee2aaSAndroid Build Coastguard Worker         if (!this->writeStatement(*i.ifFalse())) {
2014*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
2015*c8dee2aaSAndroid Build Coastguard Worker         }
2016*c8dee2aaSAndroid Build Coastguard Worker 
2017*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.label(exitLabelID);
2018*c8dee2aaSAndroid Build Coastguard Worker     }
2019*c8dee2aaSAndroid Build Coastguard Worker 
2020*c8dee2aaSAndroid Build Coastguard Worker     // Jettison the test-expression.
2021*c8dee2aaSAndroid Build Coastguard Worker     this->discardExpression(/*slots=*/1);
2022*c8dee2aaSAndroid Build Coastguard Worker     return true;
2023*c8dee2aaSAndroid Build Coastguard Worker }
2024*c8dee2aaSAndroid Build Coastguard Worker 
writeIfStatement(const IfStatement & i)2025*c8dee2aaSAndroid Build Coastguard Worker bool Generator::writeIfStatement(const IfStatement& i) {
2026*c8dee2aaSAndroid Build Coastguard Worker     // If the test condition is known to be uniform, we can skip over the untrue portion entirely.
2027*c8dee2aaSAndroid Build Coastguard Worker     if (Analysis::IsDynamicallyUniformExpression(*i.test())) {
2028*c8dee2aaSAndroid Build Coastguard Worker         return this->writeDynamicallyUniformIfStatement(i);
2029*c8dee2aaSAndroid Build Coastguard Worker     }
2030*c8dee2aaSAndroid Build Coastguard Worker 
2031*c8dee2aaSAndroid Build Coastguard Worker     // Save the current condition-mask.
2032*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.enableExecutionMaskWrites();
2033*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.push_condition_mask();
2034*c8dee2aaSAndroid Build Coastguard Worker 
2035*c8dee2aaSAndroid Build Coastguard Worker     // Push the test condition mask.
2036*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushExpression(*i.test())) {
2037*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
2038*c8dee2aaSAndroid Build Coastguard Worker     }
2039*c8dee2aaSAndroid Build Coastguard Worker 
2040*c8dee2aaSAndroid Build Coastguard Worker     // Merge the current condition-mask with the test condition, then run the if-true branch.
2041*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.merge_condition_mask();
2042*c8dee2aaSAndroid Build Coastguard Worker     if (!this->writeStatement(*i.ifTrue())) {
2043*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
2044*c8dee2aaSAndroid Build Coastguard Worker     }
2045*c8dee2aaSAndroid Build Coastguard Worker 
2046*c8dee2aaSAndroid Build Coastguard Worker     if (i.ifFalse()) {
2047*c8dee2aaSAndroid Build Coastguard Worker         // Apply the inverse condition-mask. Then run the if-false branch.
2048*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.merge_inv_condition_mask();
2049*c8dee2aaSAndroid Build Coastguard Worker         if (!this->writeStatement(*i.ifFalse())) {
2050*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
2051*c8dee2aaSAndroid Build Coastguard Worker         }
2052*c8dee2aaSAndroid Build Coastguard Worker     }
2053*c8dee2aaSAndroid Build Coastguard Worker 
2054*c8dee2aaSAndroid Build Coastguard Worker     // Jettison the test-expression, and restore the the condition-mask.
2055*c8dee2aaSAndroid Build Coastguard Worker     this->discardExpression(/*slots=*/1);
2056*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.pop_condition_mask();
2057*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.disableExecutionMaskWrites();
2058*c8dee2aaSAndroid Build Coastguard Worker 
2059*c8dee2aaSAndroid Build Coastguard Worker     return true;
2060*c8dee2aaSAndroid Build Coastguard Worker }
2061*c8dee2aaSAndroid Build Coastguard Worker 
writeReturnStatement(const ReturnStatement & r)2062*c8dee2aaSAndroid Build Coastguard Worker bool Generator::writeReturnStatement(const ReturnStatement& r) {
2063*c8dee2aaSAndroid Build Coastguard Worker     if (r.expression()) {
2064*c8dee2aaSAndroid Build Coastguard Worker         if (!this->pushExpression(*r.expression())) {
2065*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
2066*c8dee2aaSAndroid Build Coastguard Worker         }
2067*c8dee2aaSAndroid Build Coastguard Worker         if (this->needsFunctionResultSlots(fCurrentFunction)) {
2068*c8dee2aaSAndroid Build Coastguard Worker             this->popToSlotRange(fCurrentFunctionResult);
2069*c8dee2aaSAndroid Build Coastguard Worker         }
2070*c8dee2aaSAndroid Build Coastguard Worker     }
2071*c8dee2aaSAndroid Build Coastguard Worker     if (fBuilder.executionMaskWritesAreEnabled() && this->needsReturnMask(fCurrentFunction)) {
2072*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.mask_off_return_mask();
2073*c8dee2aaSAndroid Build Coastguard Worker     }
2074*c8dee2aaSAndroid Build Coastguard Worker     return true;
2075*c8dee2aaSAndroid Build Coastguard Worker }
2076*c8dee2aaSAndroid Build Coastguard Worker 
writeSwitchStatement(const SwitchStatement & s)2077*c8dee2aaSAndroid Build Coastguard Worker bool Generator::writeSwitchStatement(const SwitchStatement& s) {
2078*c8dee2aaSAndroid Build Coastguard Worker     const StatementArray& cases = s.cases();
2079*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(std::all_of(cases.begin(), cases.end(), [](const std::unique_ptr<Statement>& stmt) {
2080*c8dee2aaSAndroid Build Coastguard Worker         return stmt->is<SwitchCase>();
2081*c8dee2aaSAndroid Build Coastguard Worker     }));
2082*c8dee2aaSAndroid Build Coastguard Worker 
2083*c8dee2aaSAndroid Build Coastguard Worker     // Set up a break target.
2084*c8dee2aaSAndroid Build Coastguard Worker     AutoLoopTarget breakTarget(this, &fCurrentBreakTarget);
2085*c8dee2aaSAndroid Build Coastguard Worker 
2086*c8dee2aaSAndroid Build Coastguard Worker     // Save off the original loop mask.
2087*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.enableExecutionMaskWrites();
2088*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.push_loop_mask();
2089*c8dee2aaSAndroid Build Coastguard Worker 
2090*c8dee2aaSAndroid Build Coastguard Worker     // Push the switch-case value, and write a default-mask that enables every lane which already
2091*c8dee2aaSAndroid Build Coastguard Worker     // has an active loop mask. As we match cases, the default mask will get pared down.
2092*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushExpression(*s.value())) {
2093*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
2094*c8dee2aaSAndroid Build Coastguard Worker     }
2095*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.push_loop_mask();
2096*c8dee2aaSAndroid Build Coastguard Worker 
2097*c8dee2aaSAndroid Build Coastguard Worker     // Zero out the loop mask; each case op will re-enable it as we go.
2098*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.mask_off_loop_mask();
2099*c8dee2aaSAndroid Build Coastguard Worker 
2100*c8dee2aaSAndroid Build Coastguard Worker     // Write each switch-case.
2101*c8dee2aaSAndroid Build Coastguard Worker     bool foundDefaultCase = false;
2102*c8dee2aaSAndroid Build Coastguard Worker     for (const std::unique_ptr<Statement>& stmt : cases) {
2103*c8dee2aaSAndroid Build Coastguard Worker         int skipLabelID = fBuilder.nextLabelID();
2104*c8dee2aaSAndroid Build Coastguard Worker 
2105*c8dee2aaSAndroid Build Coastguard Worker         const SwitchCase& sc = stmt->as<SwitchCase>();
2106*c8dee2aaSAndroid Build Coastguard Worker         if (sc.isDefault()) {
2107*c8dee2aaSAndroid Build Coastguard Worker             foundDefaultCase = true;
2108*c8dee2aaSAndroid Build Coastguard Worker             if (stmt.get() != cases.back().get()) {
2109*c8dee2aaSAndroid Build Coastguard Worker                 // We only support a default case when it is the very last case. If that changes,
2110*c8dee2aaSAndroid Build Coastguard Worker                 // this logic will need to be updated.
2111*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
2112*c8dee2aaSAndroid Build Coastguard Worker             }
2113*c8dee2aaSAndroid Build Coastguard Worker             // Keep whatever lanes are executing now, and also enable any lanes in the default mask.
2114*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.pop_and_reenable_loop_mask();
2115*c8dee2aaSAndroid Build Coastguard Worker             // Execute the switch-case block, if any lanes are alive to see it.
2116*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.branch_if_no_lanes_active(skipLabelID);
2117*c8dee2aaSAndroid Build Coastguard Worker             if (!this->writeStatement(*sc.statement())) {
2118*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
2119*c8dee2aaSAndroid Build Coastguard Worker             }
2120*c8dee2aaSAndroid Build Coastguard Worker         } else {
2121*c8dee2aaSAndroid Build Coastguard Worker             // The case-op will enable the loop mask if the switch-value matches, and mask off lanes
2122*c8dee2aaSAndroid Build Coastguard Worker             // from the default-mask.
2123*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.case_op(sc.value());
2124*c8dee2aaSAndroid Build Coastguard Worker             // Execute the switch-case block, if any lanes are alive to see it.
2125*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.branch_if_no_lanes_active(skipLabelID);
2126*c8dee2aaSAndroid Build Coastguard Worker             if (!this->writeStatement(*sc.statement())) {
2127*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
2128*c8dee2aaSAndroid Build Coastguard Worker             }
2129*c8dee2aaSAndroid Build Coastguard Worker         }
2130*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.label(skipLabelID);
2131*c8dee2aaSAndroid Build Coastguard Worker     }
2132*c8dee2aaSAndroid Build Coastguard Worker 
2133*c8dee2aaSAndroid Build Coastguard Worker     // Jettison the switch value, and the default case mask if it was never consumed above.
2134*c8dee2aaSAndroid Build Coastguard Worker     this->discardExpression(/*slots=*/foundDefaultCase ? 1 : 2);
2135*c8dee2aaSAndroid Build Coastguard Worker 
2136*c8dee2aaSAndroid Build Coastguard Worker     // If we hit a break statement on all lanes, we will branch here to escape from the switch.
2137*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.label(breakTarget.labelID());
2138*c8dee2aaSAndroid Build Coastguard Worker 
2139*c8dee2aaSAndroid Build Coastguard Worker     // Restore the loop mask.
2140*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.pop_loop_mask();
2141*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.disableExecutionMaskWrites();
2142*c8dee2aaSAndroid Build Coastguard Worker     return true;
2143*c8dee2aaSAndroid Build Coastguard Worker }
2144*c8dee2aaSAndroid Build Coastguard Worker 
writeImmutableVarDeclaration(const VarDeclaration & d)2145*c8dee2aaSAndroid Build Coastguard Worker bool Generator::writeImmutableVarDeclaration(const VarDeclaration& d) {
2146*c8dee2aaSAndroid Build Coastguard Worker     // In a debugging session, we expect debug traces for a variable declaration to appear, even if
2147*c8dee2aaSAndroid Build Coastguard Worker     // it's constant, so we don't use immutable slots for variables when tracing is on.
2148*c8dee2aaSAndroid Build Coastguard Worker     if (this->shouldWriteTraceOps()) {
2149*c8dee2aaSAndroid Build Coastguard Worker         return false;
2150*c8dee2aaSAndroid Build Coastguard Worker     }
2151*c8dee2aaSAndroid Build Coastguard Worker 
2152*c8dee2aaSAndroid Build Coastguard Worker     // Find the constant value for this variable.
2153*c8dee2aaSAndroid Build Coastguard Worker     const Expression* initialValue = ConstantFolder::GetConstantValueForVariable(*d.value());
2154*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(initialValue);
2155*c8dee2aaSAndroid Build Coastguard Worker 
2156*c8dee2aaSAndroid Build Coastguard Worker     // For a variable to be immutable, it cannot be written-to besides its initial declaration.
2157*c8dee2aaSAndroid Build Coastguard Worker     ProgramUsage::VariableCounts counts = fProgram.fUsage->get(*d.var());
2158*c8dee2aaSAndroid Build Coastguard Worker     if (counts.fWrite != 1) {
2159*c8dee2aaSAndroid Build Coastguard Worker         return false;
2160*c8dee2aaSAndroid Build Coastguard Worker     }
2161*c8dee2aaSAndroid Build Coastguard Worker 
2162*c8dee2aaSAndroid Build Coastguard Worker     STArray<16, ImmutableBits> immutableValues;
2163*c8dee2aaSAndroid Build Coastguard Worker     if (!this->getImmutableValueForExpression(*initialValue, &immutableValues)) {
2164*c8dee2aaSAndroid Build Coastguard Worker         return false;
2165*c8dee2aaSAndroid Build Coastguard Worker     }
2166*c8dee2aaSAndroid Build Coastguard Worker 
2167*c8dee2aaSAndroid Build Coastguard Worker     fImmutableVariables.add(d.var());
2168*c8dee2aaSAndroid Build Coastguard Worker 
2169*c8dee2aaSAndroid Build Coastguard Worker     std::optional<SlotRange> preexistingSlots = this->findPreexistingImmutableData(immutableValues);
2170*c8dee2aaSAndroid Build Coastguard Worker     if (preexistingSlots.has_value()) {
2171*c8dee2aaSAndroid Build Coastguard Worker         // Associate this variable with a preexisting range of immutable data (no new data or code).
2172*c8dee2aaSAndroid Build Coastguard Worker         fImmutableSlots.mapVariableToSlots(*d.var(), *preexistingSlots);
2173*c8dee2aaSAndroid Build Coastguard Worker     } else {
2174*c8dee2aaSAndroid Build Coastguard Worker         // Write out the constant value back to immutable slots. (This generates data, but no
2175*c8dee2aaSAndroid Build Coastguard Worker         // runtime code.)
2176*c8dee2aaSAndroid Build Coastguard Worker         SlotRange slots = this->getImmutableSlots(*d.var());
2177*c8dee2aaSAndroid Build Coastguard Worker         this->storeImmutableValueToSlots(immutableValues, slots);
2178*c8dee2aaSAndroid Build Coastguard Worker     }
2179*c8dee2aaSAndroid Build Coastguard Worker 
2180*c8dee2aaSAndroid Build Coastguard Worker     return true;
2181*c8dee2aaSAndroid Build Coastguard Worker }
2182*c8dee2aaSAndroid Build Coastguard Worker 
writeVarDeclaration(const VarDeclaration & v)2183*c8dee2aaSAndroid Build Coastguard Worker bool Generator::writeVarDeclaration(const VarDeclaration& v) {
2184*c8dee2aaSAndroid Build Coastguard Worker     if (v.value()) {
2185*c8dee2aaSAndroid Build Coastguard Worker         // If a variable never actually changes, we can make it immutable.
2186*c8dee2aaSAndroid Build Coastguard Worker         if (this->writeImmutableVarDeclaration(v)) {
2187*c8dee2aaSAndroid Build Coastguard Worker             return true;
2188*c8dee2aaSAndroid Build Coastguard Worker         }
2189*c8dee2aaSAndroid Build Coastguard Worker         // This is a real variable which can change over the course of execution.
2190*c8dee2aaSAndroid Build Coastguard Worker         if (!this->pushExpression(*v.value())) {
2191*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
2192*c8dee2aaSAndroid Build Coastguard Worker         }
2193*c8dee2aaSAndroid Build Coastguard Worker         this->popToSlotRangeUnmasked(this->getVariableSlots(*v.var()));
2194*c8dee2aaSAndroid Build Coastguard Worker     } else {
2195*c8dee2aaSAndroid Build Coastguard Worker         this->zeroSlotRangeUnmasked(this->getVariableSlots(*v.var()));
2196*c8dee2aaSAndroid Build Coastguard Worker     }
2197*c8dee2aaSAndroid Build Coastguard Worker     return true;
2198*c8dee2aaSAndroid Build Coastguard Worker }
2199*c8dee2aaSAndroid Build Coastguard Worker 
pushExpression(const Expression & e,bool usesResult)2200*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushExpression(const Expression& e, bool usesResult) {
2201*c8dee2aaSAndroid Build Coastguard Worker     switch (e.kind()) {
2202*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kBinary:
2203*c8dee2aaSAndroid Build Coastguard Worker             return this->pushBinaryExpression(e.as<BinaryExpression>());
2204*c8dee2aaSAndroid Build Coastguard Worker 
2205*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kChildCall:
2206*c8dee2aaSAndroid Build Coastguard Worker             return this->pushChildCall(e.as<ChildCall>());
2207*c8dee2aaSAndroid Build Coastguard Worker 
2208*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorArray:
2209*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorArrayCast:
2210*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorCompound:
2211*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorStruct:
2212*c8dee2aaSAndroid Build Coastguard Worker             return this->pushConstructorCompound(e.asAnyConstructor());
2213*c8dee2aaSAndroid Build Coastguard Worker 
2214*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorCompoundCast:
2215*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorScalarCast:
2216*c8dee2aaSAndroid Build Coastguard Worker             return this->pushConstructorCast(e.asAnyConstructor());
2217*c8dee2aaSAndroid Build Coastguard Worker 
2218*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorDiagonalMatrix:
2219*c8dee2aaSAndroid Build Coastguard Worker             return this->pushConstructorDiagonalMatrix(e.as<ConstructorDiagonalMatrix>());
2220*c8dee2aaSAndroid Build Coastguard Worker 
2221*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorMatrixResize:
2222*c8dee2aaSAndroid Build Coastguard Worker             return this->pushConstructorMatrixResize(e.as<ConstructorMatrixResize>());
2223*c8dee2aaSAndroid Build Coastguard Worker 
2224*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorSplat:
2225*c8dee2aaSAndroid Build Coastguard Worker             return this->pushConstructorSplat(e.as<ConstructorSplat>());
2226*c8dee2aaSAndroid Build Coastguard Worker 
2227*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kEmpty:
2228*c8dee2aaSAndroid Build Coastguard Worker             return true;
2229*c8dee2aaSAndroid Build Coastguard Worker 
2230*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kFieldAccess:
2231*c8dee2aaSAndroid Build Coastguard Worker             return this->pushFieldAccess(e.as<FieldAccess>());
2232*c8dee2aaSAndroid Build Coastguard Worker 
2233*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kFunctionCall:
2234*c8dee2aaSAndroid Build Coastguard Worker             return this->pushFunctionCall(e.as<FunctionCall>());
2235*c8dee2aaSAndroid Build Coastguard Worker 
2236*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kIndex:
2237*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIndexExpression(e.as<IndexExpression>());
2238*c8dee2aaSAndroid Build Coastguard Worker 
2239*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kLiteral:
2240*c8dee2aaSAndroid Build Coastguard Worker             return this->pushLiteral(e.as<Literal>());
2241*c8dee2aaSAndroid Build Coastguard Worker 
2242*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kPrefix:
2243*c8dee2aaSAndroid Build Coastguard Worker             return this->pushPrefixExpression(e.as<PrefixExpression>());
2244*c8dee2aaSAndroid Build Coastguard Worker 
2245*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kPostfix:
2246*c8dee2aaSAndroid Build Coastguard Worker             return this->pushPostfixExpression(e.as<PostfixExpression>(), usesResult);
2247*c8dee2aaSAndroid Build Coastguard Worker 
2248*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kSwizzle:
2249*c8dee2aaSAndroid Build Coastguard Worker             return this->pushSwizzle(e.as<Swizzle>());
2250*c8dee2aaSAndroid Build Coastguard Worker 
2251*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kTernary:
2252*c8dee2aaSAndroid Build Coastguard Worker             return this->pushTernaryExpression(e.as<TernaryExpression>());
2253*c8dee2aaSAndroid Build Coastguard Worker 
2254*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kVariableReference:
2255*c8dee2aaSAndroid Build Coastguard Worker             return this->pushVariableReference(e.as<VariableReference>());
2256*c8dee2aaSAndroid Build Coastguard Worker 
2257*c8dee2aaSAndroid Build Coastguard Worker         default:
2258*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
2259*c8dee2aaSAndroid Build Coastguard Worker     }
2260*c8dee2aaSAndroid Build Coastguard Worker }
2261*c8dee2aaSAndroid Build Coastguard Worker 
GetTypedOp(const SkSL::Type & type,const TypedOps & ops)2262*c8dee2aaSAndroid Build Coastguard Worker BuilderOp Generator::GetTypedOp(const SkSL::Type& type, const TypedOps& ops) {
2263*c8dee2aaSAndroid Build Coastguard Worker     switch (type.componentType().numberKind()) {
2264*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kFloat:    return ops.fFloatOp;
2265*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kSigned:   return ops.fSignedOp;
2266*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kUnsigned: return ops.fUnsignedOp;
2267*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kBoolean:  return ops.fBooleanOp;
2268*c8dee2aaSAndroid Build Coastguard Worker         default:                          return BuilderOp::unsupported;
2269*c8dee2aaSAndroid Build Coastguard Worker     }
2270*c8dee2aaSAndroid Build Coastguard Worker }
2271*c8dee2aaSAndroid Build Coastguard Worker 
unaryOp(const SkSL::Type & type,const TypedOps & ops)2272*c8dee2aaSAndroid Build Coastguard Worker bool Generator::unaryOp(const SkSL::Type& type, const TypedOps& ops) {
2273*c8dee2aaSAndroid Build Coastguard Worker     BuilderOp op = GetTypedOp(type, ops);
2274*c8dee2aaSAndroid Build Coastguard Worker     if (op == BuilderOp::unsupported) {
2275*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
2276*c8dee2aaSAndroid Build Coastguard Worker     }
2277*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.unary_op(op, type.slotCount());
2278*c8dee2aaSAndroid Build Coastguard Worker     return true;
2279*c8dee2aaSAndroid Build Coastguard Worker }
2280*c8dee2aaSAndroid Build Coastguard Worker 
binaryOp(const SkSL::Type & type,const TypedOps & ops)2281*c8dee2aaSAndroid Build Coastguard Worker bool Generator::binaryOp(const SkSL::Type& type, const TypedOps& ops) {
2282*c8dee2aaSAndroid Build Coastguard Worker     BuilderOp op = GetTypedOp(type, ops);
2283*c8dee2aaSAndroid Build Coastguard Worker     if (op == BuilderOp::unsupported) {
2284*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
2285*c8dee2aaSAndroid Build Coastguard Worker     }
2286*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.binary_op(op, type.slotCount());
2287*c8dee2aaSAndroid Build Coastguard Worker     return true;
2288*c8dee2aaSAndroid Build Coastguard Worker }
2289*c8dee2aaSAndroid Build Coastguard Worker 
ternaryOp(const SkSL::Type & type,const TypedOps & ops)2290*c8dee2aaSAndroid Build Coastguard Worker bool Generator::ternaryOp(const SkSL::Type& type, const TypedOps& ops) {
2291*c8dee2aaSAndroid Build Coastguard Worker     BuilderOp op = GetTypedOp(type, ops);
2292*c8dee2aaSAndroid Build Coastguard Worker     if (op == BuilderOp::unsupported) {
2293*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
2294*c8dee2aaSAndroid Build Coastguard Worker     }
2295*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.ternary_op(op, type.slotCount());
2296*c8dee2aaSAndroid Build Coastguard Worker     return true;
2297*c8dee2aaSAndroid Build Coastguard Worker }
2298*c8dee2aaSAndroid Build Coastguard Worker 
foldWithMultiOp(BuilderOp op,int elements)2299*c8dee2aaSAndroid Build Coastguard Worker void Generator::foldWithMultiOp(BuilderOp op, int elements) {
2300*c8dee2aaSAndroid Build Coastguard Worker     // Fold the top N elements on the stack using an op that supports multiple slots, e.g.:
2301*c8dee2aaSAndroid Build Coastguard Worker     // (A + B + C + D) -> add_2_floats $0..1 += $2..3
2302*c8dee2aaSAndroid Build Coastguard Worker     //                    add_float    $0    += $1
2303*c8dee2aaSAndroid Build Coastguard Worker     for (; elements >= 8; elements -= 4) {
2304*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.binary_op(op, /*slots=*/4);
2305*c8dee2aaSAndroid Build Coastguard Worker     }
2306*c8dee2aaSAndroid Build Coastguard Worker     for (; elements >= 6; elements -= 3) {
2307*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.binary_op(op, /*slots=*/3);
2308*c8dee2aaSAndroid Build Coastguard Worker     }
2309*c8dee2aaSAndroid Build Coastguard Worker     for (; elements >= 4; elements -= 2) {
2310*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.binary_op(op, /*slots=*/2);
2311*c8dee2aaSAndroid Build Coastguard Worker     }
2312*c8dee2aaSAndroid Build Coastguard Worker     for (; elements >= 2; elements -= 1) {
2313*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.binary_op(op, /*slots=*/1);
2314*c8dee2aaSAndroid Build Coastguard Worker     }
2315*c8dee2aaSAndroid Build Coastguard Worker }
2316*c8dee2aaSAndroid Build Coastguard Worker 
pushLValueOrExpression(LValue * lvalue,const Expression & expr)2317*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushLValueOrExpression(LValue* lvalue, const Expression& expr) {
2318*c8dee2aaSAndroid Build Coastguard Worker     return lvalue ? this->push(*lvalue)
2319*c8dee2aaSAndroid Build Coastguard Worker                   : this->pushExpression(expr);
2320*c8dee2aaSAndroid Build Coastguard Worker }
2321*c8dee2aaSAndroid Build Coastguard Worker 
pushMatrixMultiply(LValue * lvalue,const Expression & left,const Expression & right,int leftColumns,int leftRows,int rightColumns,int rightRows)2322*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushMatrixMultiply(LValue* lvalue,
2323*c8dee2aaSAndroid Build Coastguard Worker                                    const Expression& left,
2324*c8dee2aaSAndroid Build Coastguard Worker                                    const Expression& right,
2325*c8dee2aaSAndroid Build Coastguard Worker                                    int leftColumns,
2326*c8dee2aaSAndroid Build Coastguard Worker                                    int leftRows,
2327*c8dee2aaSAndroid Build Coastguard Worker                                    int rightColumns,
2328*c8dee2aaSAndroid Build Coastguard Worker                                    int rightRows) {
2329*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(left.type().isMatrix() || left.type().isVector());
2330*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(right.type().isMatrix() || right.type().isVector());
2331*c8dee2aaSAndroid Build Coastguard Worker 
2332*c8dee2aaSAndroid Build Coastguard Worker     // Insert padding space on the stack to hold the result.
2333*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.pad_stack(rightColumns * leftRows);
2334*c8dee2aaSAndroid Build Coastguard Worker 
2335*c8dee2aaSAndroid Build Coastguard Worker     // Push the left and right matrices onto the stack.
2336*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushLValueOrExpression(lvalue, left) || !this->pushExpression(right)) {
2337*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
2338*c8dee2aaSAndroid Build Coastguard Worker     }
2339*c8dee2aaSAndroid Build Coastguard Worker 
2340*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.matrix_multiply(leftColumns, leftRows, rightColumns, rightRows);
2341*c8dee2aaSAndroid Build Coastguard Worker 
2342*c8dee2aaSAndroid Build Coastguard Worker     // If this multiply was actually an assignment (via *=), write the result back to the lvalue.
2343*c8dee2aaSAndroid Build Coastguard Worker     return lvalue ? this->store(*lvalue)
2344*c8dee2aaSAndroid Build Coastguard Worker                   : true;
2345*c8dee2aaSAndroid Build Coastguard Worker }
2346*c8dee2aaSAndroid Build Coastguard Worker 
foldComparisonOp(Operator op,int elements)2347*c8dee2aaSAndroid Build Coastguard Worker void Generator::foldComparisonOp(Operator op, int elements) {
2348*c8dee2aaSAndroid Build Coastguard Worker     switch (op.kind()) {
2349*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::EQEQ:
2350*c8dee2aaSAndroid Build Coastguard Worker             // equal(x,y) returns a vector; use & to fold into a scalar.
2351*c8dee2aaSAndroid Build Coastguard Worker             this->foldWithMultiOp(BuilderOp::bitwise_and_n_ints, elements);
2352*c8dee2aaSAndroid Build Coastguard Worker             break;
2353*c8dee2aaSAndroid Build Coastguard Worker 
2354*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::NEQ:
2355*c8dee2aaSAndroid Build Coastguard Worker             // notEqual(x,y) returns a vector; use | to fold into a scalar.
2356*c8dee2aaSAndroid Build Coastguard Worker             this->foldWithMultiOp(BuilderOp::bitwise_or_n_ints, elements);
2357*c8dee2aaSAndroid Build Coastguard Worker             break;
2358*c8dee2aaSAndroid Build Coastguard Worker 
2359*c8dee2aaSAndroid Build Coastguard Worker         default:
2360*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAIL("comparison only allows == and !=");
2361*c8dee2aaSAndroid Build Coastguard Worker             break;
2362*c8dee2aaSAndroid Build Coastguard Worker     }
2363*c8dee2aaSAndroid Build Coastguard Worker }
2364*c8dee2aaSAndroid Build Coastguard Worker 
pushStructuredComparison(LValue * left,Operator op,LValue * right,const Type & type)2365*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushStructuredComparison(LValue* left,
2366*c8dee2aaSAndroid Build Coastguard Worker                                          Operator op,
2367*c8dee2aaSAndroid Build Coastguard Worker                                          LValue* right,
2368*c8dee2aaSAndroid Build Coastguard Worker                                          const Type& type) {
2369*c8dee2aaSAndroid Build Coastguard Worker     if (type.isStruct()) {
2370*c8dee2aaSAndroid Build Coastguard Worker         // Compare every field in the struct.
2371*c8dee2aaSAndroid Build Coastguard Worker         SkSpan<const Field> fields = type.fields();
2372*c8dee2aaSAndroid Build Coastguard Worker         int currentSlot = 0;
2373*c8dee2aaSAndroid Build Coastguard Worker         for (size_t index = 0; index < fields.size(); ++index) {
2374*c8dee2aaSAndroid Build Coastguard Worker             const Type& fieldType = *fields[index].fType;
2375*c8dee2aaSAndroid Build Coastguard Worker             const int   fieldSlotCount = fieldType.slotCount();
2376*c8dee2aaSAndroid Build Coastguard Worker             UnownedLValueSlice fieldLeft {left,  currentSlot, fieldSlotCount};
2377*c8dee2aaSAndroid Build Coastguard Worker             UnownedLValueSlice fieldRight{right, currentSlot, fieldSlotCount};
2378*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushStructuredComparison(&fieldLeft, op, &fieldRight, fieldType)) {
2379*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
2380*c8dee2aaSAndroid Build Coastguard Worker             }
2381*c8dee2aaSAndroid Build Coastguard Worker             currentSlot += fieldSlotCount;
2382*c8dee2aaSAndroid Build Coastguard Worker         }
2383*c8dee2aaSAndroid Build Coastguard Worker 
2384*c8dee2aaSAndroid Build Coastguard Worker         this->foldComparisonOp(op, fields.size());
2385*c8dee2aaSAndroid Build Coastguard Worker         return true;
2386*c8dee2aaSAndroid Build Coastguard Worker     }
2387*c8dee2aaSAndroid Build Coastguard Worker 
2388*c8dee2aaSAndroid Build Coastguard Worker     if (type.isArray()) {
2389*c8dee2aaSAndroid Build Coastguard Worker         const Type& indexedType = type.componentType();
2390*c8dee2aaSAndroid Build Coastguard Worker         if (indexedType.numberKind() == Type::NumberKind::kNonnumeric) {
2391*c8dee2aaSAndroid Build Coastguard Worker             // Compare every element in the array.
2392*c8dee2aaSAndroid Build Coastguard Worker             const int indexedSlotCount = indexedType.slotCount();
2393*c8dee2aaSAndroid Build Coastguard Worker             int       currentSlot = 0;
2394*c8dee2aaSAndroid Build Coastguard Worker             for (int index = 0; index < type.columns(); ++index) {
2395*c8dee2aaSAndroid Build Coastguard Worker                 UnownedLValueSlice indexedLeft {left,  currentSlot, indexedSlotCount};
2396*c8dee2aaSAndroid Build Coastguard Worker                 UnownedLValueSlice indexedRight{right, currentSlot, indexedSlotCount};
2397*c8dee2aaSAndroid Build Coastguard Worker                 if (!this->pushStructuredComparison(&indexedLeft, op, &indexedRight, indexedType)) {
2398*c8dee2aaSAndroid Build Coastguard Worker                     return unsupported();
2399*c8dee2aaSAndroid Build Coastguard Worker                 }
2400*c8dee2aaSAndroid Build Coastguard Worker                 currentSlot += indexedSlotCount;
2401*c8dee2aaSAndroid Build Coastguard Worker             }
2402*c8dee2aaSAndroid Build Coastguard Worker 
2403*c8dee2aaSAndroid Build Coastguard Worker             this->foldComparisonOp(op, type.columns());
2404*c8dee2aaSAndroid Build Coastguard Worker             return true;
2405*c8dee2aaSAndroid Build Coastguard Worker         }
2406*c8dee2aaSAndroid Build Coastguard Worker     }
2407*c8dee2aaSAndroid Build Coastguard Worker 
2408*c8dee2aaSAndroid Build Coastguard Worker     // We've winnowed down to a single element, or an array of homogeneous numeric elements.
2409*c8dee2aaSAndroid Build Coastguard Worker     // Push the elements onto the stack, then compare them.
2410*c8dee2aaSAndroid Build Coastguard Worker     if (!this->push(*left) || !this->push(*right)) {
2411*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
2412*c8dee2aaSAndroid Build Coastguard Worker     }
2413*c8dee2aaSAndroid Build Coastguard Worker     switch (op.kind()) {
2414*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::EQEQ:
2415*c8dee2aaSAndroid Build Coastguard Worker             if (!this->binaryOp(type, kEqualOps)) {
2416*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
2417*c8dee2aaSAndroid Build Coastguard Worker             }
2418*c8dee2aaSAndroid Build Coastguard Worker             break;
2419*c8dee2aaSAndroid Build Coastguard Worker 
2420*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::NEQ:
2421*c8dee2aaSAndroid Build Coastguard Worker             if (!this->binaryOp(type, kNotEqualOps)) {
2422*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
2423*c8dee2aaSAndroid Build Coastguard Worker             }
2424*c8dee2aaSAndroid Build Coastguard Worker             break;
2425*c8dee2aaSAndroid Build Coastguard Worker 
2426*c8dee2aaSAndroid Build Coastguard Worker         default:
2427*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAIL("comparison only allows == and !=");
2428*c8dee2aaSAndroid Build Coastguard Worker             break;
2429*c8dee2aaSAndroid Build Coastguard Worker     }
2430*c8dee2aaSAndroid Build Coastguard Worker 
2431*c8dee2aaSAndroid Build Coastguard Worker     this->foldComparisonOp(op, type.slotCount());
2432*c8dee2aaSAndroid Build Coastguard Worker     return true;
2433*c8dee2aaSAndroid Build Coastguard Worker }
2434*c8dee2aaSAndroid Build Coastguard Worker 
pushBinaryExpression(const BinaryExpression & e)2435*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushBinaryExpression(const BinaryExpression& e) {
2436*c8dee2aaSAndroid Build Coastguard Worker     return this->pushBinaryExpression(*e.left(), e.getOperator(), *e.right());
2437*c8dee2aaSAndroid Build Coastguard Worker }
2438*c8dee2aaSAndroid Build Coastguard Worker 
pushBinaryExpression(const Expression & left,Operator op,const Expression & right)2439*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushBinaryExpression(const Expression& left, Operator op, const Expression& right) {
2440*c8dee2aaSAndroid Build Coastguard Worker     switch (op.kind()) {
2441*c8dee2aaSAndroid Build Coastguard Worker         // Rewrite greater-than ops as their less-than equivalents.
2442*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::GT:
2443*c8dee2aaSAndroid Build Coastguard Worker             return this->pushBinaryExpression(right, OperatorKind::LT, left);
2444*c8dee2aaSAndroid Build Coastguard Worker 
2445*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::GTEQ:
2446*c8dee2aaSAndroid Build Coastguard Worker             return this->pushBinaryExpression(right, OperatorKind::LTEQ, left);
2447*c8dee2aaSAndroid Build Coastguard Worker 
2448*c8dee2aaSAndroid Build Coastguard Worker         // Handle struct and array comparisons.
2449*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::EQEQ:
2450*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::NEQ:
2451*c8dee2aaSAndroid Build Coastguard Worker             if (left.type().isStruct() || left.type().isArray()) {
2452*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(left.type().matches(right.type()));
2453*c8dee2aaSAndroid Build Coastguard Worker                 std::unique_ptr<LValue> lvLeft = this->makeLValue(left, /*allowScratch=*/true);
2454*c8dee2aaSAndroid Build Coastguard Worker                 std::unique_ptr<LValue> lvRight = this->makeLValue(right, /*allowScratch=*/true);
2455*c8dee2aaSAndroid Build Coastguard Worker                 return this->pushStructuredComparison(lvLeft.get(), op, lvRight.get(), left.type());
2456*c8dee2aaSAndroid Build Coastguard Worker             }
2457*c8dee2aaSAndroid Build Coastguard Worker             [[fallthrough]];
2458*c8dee2aaSAndroid Build Coastguard Worker 
2459*c8dee2aaSAndroid Build Coastguard Worker         // Rewrite commutative ops so that the literal is on the right-hand side. This gives the
2460*c8dee2aaSAndroid Build Coastguard Worker         // Builder more opportunities to use immediate-mode ops.
2461*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::PLUS:
2462*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::STAR:
2463*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::BITWISEAND:
2464*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::BITWISEXOR:
2465*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::LOGICALXOR: {
2466*c8dee2aaSAndroid Build Coastguard Worker             double unused;
2467*c8dee2aaSAndroid Build Coastguard Worker             if (ConstantFolder::GetConstantValue(left, &unused) &&
2468*c8dee2aaSAndroid Build Coastguard Worker                 !ConstantFolder::GetConstantValue(right, &unused)) {
2469*c8dee2aaSAndroid Build Coastguard Worker                 return this->pushBinaryExpression(right, op, left);
2470*c8dee2aaSAndroid Build Coastguard Worker             }
2471*c8dee2aaSAndroid Build Coastguard Worker             break;
2472*c8dee2aaSAndroid Build Coastguard Worker         }
2473*c8dee2aaSAndroid Build Coastguard Worker         // Emit comma expressions.
2474*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::COMMA:
2475*c8dee2aaSAndroid Build Coastguard Worker             if (Analysis::HasSideEffects(left)) {
2476*c8dee2aaSAndroid Build Coastguard Worker                 if (!this->pushExpression(left, /*usesResult=*/false)) {
2477*c8dee2aaSAndroid Build Coastguard Worker                     return unsupported();
2478*c8dee2aaSAndroid Build Coastguard Worker                 }
2479*c8dee2aaSAndroid Build Coastguard Worker                 this->discardExpression(left.type().slotCount());
2480*c8dee2aaSAndroid Build Coastguard Worker             }
2481*c8dee2aaSAndroid Build Coastguard Worker             return this->pushExpression(right);
2482*c8dee2aaSAndroid Build Coastguard Worker 
2483*c8dee2aaSAndroid Build Coastguard Worker         default:
2484*c8dee2aaSAndroid Build Coastguard Worker             break;
2485*c8dee2aaSAndroid Build Coastguard Worker     }
2486*c8dee2aaSAndroid Build Coastguard Worker 
2487*c8dee2aaSAndroid Build Coastguard Worker     // Handle binary expressions with mismatched types.
2488*c8dee2aaSAndroid Build Coastguard Worker     bool vectorizeLeft = false, vectorizeRight = false;
2489*c8dee2aaSAndroid Build Coastguard Worker     if (!left.type().matches(right.type())) {
2490*c8dee2aaSAndroid Build Coastguard Worker         if (left.type().componentType().numberKind() != right.type().componentType().numberKind()) {
2491*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
2492*c8dee2aaSAndroid Build Coastguard Worker         }
2493*c8dee2aaSAndroid Build Coastguard Worker         if (left.type().isScalar() && (right.type().isVector() || right.type().isMatrix())) {
2494*c8dee2aaSAndroid Build Coastguard Worker             vectorizeLeft = true;
2495*c8dee2aaSAndroid Build Coastguard Worker         } else if ((left.type().isVector() || left.type().isMatrix()) && right.type().isScalar()) {
2496*c8dee2aaSAndroid Build Coastguard Worker             vectorizeRight = true;
2497*c8dee2aaSAndroid Build Coastguard Worker         }
2498*c8dee2aaSAndroid Build Coastguard Worker     }
2499*c8dee2aaSAndroid Build Coastguard Worker 
2500*c8dee2aaSAndroid Build Coastguard Worker     const Type& type = vectorizeLeft ? right.type() : left.type();
2501*c8dee2aaSAndroid Build Coastguard Worker 
2502*c8dee2aaSAndroid Build Coastguard Worker     // If this is an assignment...
2503*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<LValue> lvalue;
2504*c8dee2aaSAndroid Build Coastguard Worker     if (op.isAssignment()) {
2505*c8dee2aaSAndroid Build Coastguard Worker         // ... turn the left side into an lvalue.
2506*c8dee2aaSAndroid Build Coastguard Worker         lvalue = this->makeLValue(left);
2507*c8dee2aaSAndroid Build Coastguard Worker         if (!lvalue) {
2508*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
2509*c8dee2aaSAndroid Build Coastguard Worker         }
2510*c8dee2aaSAndroid Build Coastguard Worker 
2511*c8dee2aaSAndroid Build Coastguard Worker         // Handle simple assignment (`var = expr`).
2512*c8dee2aaSAndroid Build Coastguard Worker         if (op.kind() == OperatorKind::EQ) {
2513*c8dee2aaSAndroid Build Coastguard Worker             return this->pushExpression(right) &&
2514*c8dee2aaSAndroid Build Coastguard Worker                    this->store(*lvalue);
2515*c8dee2aaSAndroid Build Coastguard Worker         }
2516*c8dee2aaSAndroid Build Coastguard Worker 
2517*c8dee2aaSAndroid Build Coastguard Worker         // Strip off the assignment from the op (turning += into +).
2518*c8dee2aaSAndroid Build Coastguard Worker         op = op.removeAssignment();
2519*c8dee2aaSAndroid Build Coastguard Worker     }
2520*c8dee2aaSAndroid Build Coastguard Worker 
2521*c8dee2aaSAndroid Build Coastguard Worker     // Handle matrix multiplication (MxM/MxV/VxM).
2522*c8dee2aaSAndroid Build Coastguard Worker     if (op.kind() == OperatorKind::STAR) {
2523*c8dee2aaSAndroid Build Coastguard Worker         // Matrix * matrix:
2524*c8dee2aaSAndroid Build Coastguard Worker         if (left.type().isMatrix() && right.type().isMatrix()) {
2525*c8dee2aaSAndroid Build Coastguard Worker             return this->pushMatrixMultiply(lvalue.get(), left, right,
2526*c8dee2aaSAndroid Build Coastguard Worker                                             left.type().columns(), left.type().rows(),
2527*c8dee2aaSAndroid Build Coastguard Worker                                             right.type().columns(), right.type().rows());
2528*c8dee2aaSAndroid Build Coastguard Worker         }
2529*c8dee2aaSAndroid Build Coastguard Worker 
2530*c8dee2aaSAndroid Build Coastguard Worker         // Vector * matrix:
2531*c8dee2aaSAndroid Build Coastguard Worker         if (left.type().isVector() && right.type().isMatrix()) {
2532*c8dee2aaSAndroid Build Coastguard Worker             return this->pushMatrixMultiply(lvalue.get(), left, right,
2533*c8dee2aaSAndroid Build Coastguard Worker                                             left.type().columns(), 1,
2534*c8dee2aaSAndroid Build Coastguard Worker                                             right.type().columns(), right.type().rows());
2535*c8dee2aaSAndroid Build Coastguard Worker         }
2536*c8dee2aaSAndroid Build Coastguard Worker 
2537*c8dee2aaSAndroid Build Coastguard Worker         // Matrix * vector:
2538*c8dee2aaSAndroid Build Coastguard Worker         if (left.type().isMatrix() && right.type().isVector()) {
2539*c8dee2aaSAndroid Build Coastguard Worker             return this->pushMatrixMultiply(lvalue.get(), left, right,
2540*c8dee2aaSAndroid Build Coastguard Worker                                             left.type().columns(), left.type().rows(),
2541*c8dee2aaSAndroid Build Coastguard Worker                                             1, right.type().columns());
2542*c8dee2aaSAndroid Build Coastguard Worker         }
2543*c8dee2aaSAndroid Build Coastguard Worker     }
2544*c8dee2aaSAndroid Build Coastguard Worker 
2545*c8dee2aaSAndroid Build Coastguard Worker     if (!vectorizeLeft && !vectorizeRight && !type.matches(right.type())) {
2546*c8dee2aaSAndroid Build Coastguard Worker         // We have mismatched types but don't know how to handle them.
2547*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
2548*c8dee2aaSAndroid Build Coastguard Worker     }
2549*c8dee2aaSAndroid Build Coastguard Worker 
2550*c8dee2aaSAndroid Build Coastguard Worker     // Handle binary ops which require short-circuiting.
2551*c8dee2aaSAndroid Build Coastguard Worker     switch (op.kind()) {
2552*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::LOGICALAND:
2553*c8dee2aaSAndroid Build Coastguard Worker             if (Analysis::HasSideEffects(right)) {
2554*c8dee2aaSAndroid Build Coastguard Worker                 // If the RHS has side effects, we rewrite `a && b` as `a ? b : false`. This
2555*c8dee2aaSAndroid Build Coastguard Worker                 // generates pretty solid code and gives us the required short-circuit behavior.
2556*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(!op.isAssignment());
2557*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(type.componentType().isBoolean());
2558*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(type.slotCount() == 1);  // operator&& only works with scalar types
2559*c8dee2aaSAndroid Build Coastguard Worker                 Literal falseLiteral{Position{}, 0.0, &right.type()};
2560*c8dee2aaSAndroid Build Coastguard Worker                 return this->pushTernaryExpression(left, right, falseLiteral);
2561*c8dee2aaSAndroid Build Coastguard Worker             }
2562*c8dee2aaSAndroid Build Coastguard Worker             break;
2563*c8dee2aaSAndroid Build Coastguard Worker 
2564*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::LOGICALOR:
2565*c8dee2aaSAndroid Build Coastguard Worker             if (Analysis::HasSideEffects(right)) {
2566*c8dee2aaSAndroid Build Coastguard Worker                 // If the RHS has side effects, we rewrite `a || b` as `a ? true : b`.
2567*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(!op.isAssignment());
2568*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(type.componentType().isBoolean());
2569*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(type.slotCount() == 1);  // operator|| only works with scalar types
2570*c8dee2aaSAndroid Build Coastguard Worker                 Literal trueLiteral{Position{}, 1.0, &right.type()};
2571*c8dee2aaSAndroid Build Coastguard Worker                 return this->pushTernaryExpression(left, trueLiteral, right);
2572*c8dee2aaSAndroid Build Coastguard Worker             }
2573*c8dee2aaSAndroid Build Coastguard Worker             break;
2574*c8dee2aaSAndroid Build Coastguard Worker 
2575*c8dee2aaSAndroid Build Coastguard Worker         default:
2576*c8dee2aaSAndroid Build Coastguard Worker             break;
2577*c8dee2aaSAndroid Build Coastguard Worker     }
2578*c8dee2aaSAndroid Build Coastguard Worker 
2579*c8dee2aaSAndroid Build Coastguard Worker     // Push the left- and right-expressions onto the stack.
2580*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushLValueOrExpression(lvalue.get(), left)) {
2581*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
2582*c8dee2aaSAndroid Build Coastguard Worker     }
2583*c8dee2aaSAndroid Build Coastguard Worker     if (vectorizeLeft) {
2584*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.push_duplicates(right.type().slotCount() - 1);
2585*c8dee2aaSAndroid Build Coastguard Worker     }
2586*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushExpression(right)) {
2587*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
2588*c8dee2aaSAndroid Build Coastguard Worker     }
2589*c8dee2aaSAndroid Build Coastguard Worker     if (vectorizeRight) {
2590*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.push_duplicates(left.type().slotCount() - 1);
2591*c8dee2aaSAndroid Build Coastguard Worker     }
2592*c8dee2aaSAndroid Build Coastguard Worker 
2593*c8dee2aaSAndroid Build Coastguard Worker     switch (op.kind()) {
2594*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::PLUS:
2595*c8dee2aaSAndroid Build Coastguard Worker             if (!this->binaryOp(type, kAddOps)) {
2596*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
2597*c8dee2aaSAndroid Build Coastguard Worker             }
2598*c8dee2aaSAndroid Build Coastguard Worker             break;
2599*c8dee2aaSAndroid Build Coastguard Worker 
2600*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::MINUS:
2601*c8dee2aaSAndroid Build Coastguard Worker             if (!this->binaryOp(type, kSubtractOps)) {
2602*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
2603*c8dee2aaSAndroid Build Coastguard Worker             }
2604*c8dee2aaSAndroid Build Coastguard Worker             break;
2605*c8dee2aaSAndroid Build Coastguard Worker 
2606*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::STAR:
2607*c8dee2aaSAndroid Build Coastguard Worker             if (!this->binaryOp(type, kMultiplyOps)) {
2608*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
2609*c8dee2aaSAndroid Build Coastguard Worker             }
2610*c8dee2aaSAndroid Build Coastguard Worker             break;
2611*c8dee2aaSAndroid Build Coastguard Worker 
2612*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::SLASH:
2613*c8dee2aaSAndroid Build Coastguard Worker             if (!this->binaryOp(type, kDivideOps)) {
2614*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
2615*c8dee2aaSAndroid Build Coastguard Worker             }
2616*c8dee2aaSAndroid Build Coastguard Worker             break;
2617*c8dee2aaSAndroid Build Coastguard Worker 
2618*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::LT:
2619*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::GT:
2620*c8dee2aaSAndroid Build Coastguard Worker             if (!this->binaryOp(type, kLessThanOps)) {
2621*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
2622*c8dee2aaSAndroid Build Coastguard Worker             }
2623*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(type.slotCount() == 1);  // operator< only works with scalar types
2624*c8dee2aaSAndroid Build Coastguard Worker             break;
2625*c8dee2aaSAndroid Build Coastguard Worker 
2626*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::LTEQ:
2627*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::GTEQ:
2628*c8dee2aaSAndroid Build Coastguard Worker             if (!this->binaryOp(type, kLessThanEqualOps)) {
2629*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
2630*c8dee2aaSAndroid Build Coastguard Worker             }
2631*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(type.slotCount() == 1);  // operator<= only works with scalar types
2632*c8dee2aaSAndroid Build Coastguard Worker             break;
2633*c8dee2aaSAndroid Build Coastguard Worker 
2634*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::EQEQ:
2635*c8dee2aaSAndroid Build Coastguard Worker             if (!this->binaryOp(type, kEqualOps)) {
2636*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
2637*c8dee2aaSAndroid Build Coastguard Worker             }
2638*c8dee2aaSAndroid Build Coastguard Worker             this->foldComparisonOp(op, type.slotCount());
2639*c8dee2aaSAndroid Build Coastguard Worker             break;
2640*c8dee2aaSAndroid Build Coastguard Worker 
2641*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::NEQ:
2642*c8dee2aaSAndroid Build Coastguard Worker             if (!this->binaryOp(type, kNotEqualOps)) {
2643*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
2644*c8dee2aaSAndroid Build Coastguard Worker             }
2645*c8dee2aaSAndroid Build Coastguard Worker             this->foldComparisonOp(op, type.slotCount());
2646*c8dee2aaSAndroid Build Coastguard Worker             break;
2647*c8dee2aaSAndroid Build Coastguard Worker 
2648*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::LOGICALAND:
2649*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::BITWISEAND:
2650*c8dee2aaSAndroid Build Coastguard Worker             // For logical-and, we verified above that the RHS does not have side effects, so we
2651*c8dee2aaSAndroid Build Coastguard Worker             // don't need to worry about short-circuiting side effects.
2652*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.binary_op(BuilderOp::bitwise_and_n_ints, type.slotCount());
2653*c8dee2aaSAndroid Build Coastguard Worker             break;
2654*c8dee2aaSAndroid Build Coastguard Worker 
2655*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::LOGICALOR:
2656*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::BITWISEOR:
2657*c8dee2aaSAndroid Build Coastguard Worker             // For logical-or, we verified above that the RHS does not have side effects.
2658*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.binary_op(BuilderOp::bitwise_or_n_ints, type.slotCount());
2659*c8dee2aaSAndroid Build Coastguard Worker             break;
2660*c8dee2aaSAndroid Build Coastguard Worker 
2661*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::LOGICALXOR:
2662*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::BITWISEXOR:
2663*c8dee2aaSAndroid Build Coastguard Worker             // Logical-xor does not short circuit.
2664*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.binary_op(BuilderOp::bitwise_xor_n_ints, type.slotCount());
2665*c8dee2aaSAndroid Build Coastguard Worker             break;
2666*c8dee2aaSAndroid Build Coastguard Worker 
2667*c8dee2aaSAndroid Build Coastguard Worker         default:
2668*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
2669*c8dee2aaSAndroid Build Coastguard Worker     }
2670*c8dee2aaSAndroid Build Coastguard Worker 
2671*c8dee2aaSAndroid Build Coastguard Worker     // If we have an lvalue, we need to write the result back into it.
2672*c8dee2aaSAndroid Build Coastguard Worker     return lvalue ? this->store(*lvalue)
2673*c8dee2aaSAndroid Build Coastguard Worker                   : true;
2674*c8dee2aaSAndroid Build Coastguard Worker }
2675*c8dee2aaSAndroid Build Coastguard Worker 
getImmutableBitsForSlot(const Expression & expr,size_t slot)2676*c8dee2aaSAndroid Build Coastguard Worker std::optional<Generator::ImmutableBits> Generator::getImmutableBitsForSlot(const Expression& expr,
2677*c8dee2aaSAndroid Build Coastguard Worker                                                                            size_t slot) {
2678*c8dee2aaSAndroid Build Coastguard Worker     // Determine the constant-value of the slot; bail if it isn't constant.
2679*c8dee2aaSAndroid Build Coastguard Worker     std::optional<double> v = expr.getConstantValue(slot);
2680*c8dee2aaSAndroid Build Coastguard Worker     if (!v.has_value()) {
2681*c8dee2aaSAndroid Build Coastguard Worker         return std::nullopt;
2682*c8dee2aaSAndroid Build Coastguard Worker     }
2683*c8dee2aaSAndroid Build Coastguard Worker     // Determine the number-kind of the slot, and convert the value to its bit-representation.
2684*c8dee2aaSAndroid Build Coastguard Worker     Type::NumberKind kind = expr.type().slotType(slot).numberKind();
2685*c8dee2aaSAndroid Build Coastguard Worker     double value = *v;
2686*c8dee2aaSAndroid Build Coastguard Worker     switch (kind) {
2687*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kFloat:
2688*c8dee2aaSAndroid Build Coastguard Worker             return sk_bit_cast<ImmutableBits>((float)value);
2689*c8dee2aaSAndroid Build Coastguard Worker 
2690*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kSigned:
2691*c8dee2aaSAndroid Build Coastguard Worker             return sk_bit_cast<ImmutableBits>((int32_t)value);
2692*c8dee2aaSAndroid Build Coastguard Worker 
2693*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kUnsigned:
2694*c8dee2aaSAndroid Build Coastguard Worker             return sk_bit_cast<ImmutableBits>((uint32_t)value);
2695*c8dee2aaSAndroid Build Coastguard Worker 
2696*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kBoolean:
2697*c8dee2aaSAndroid Build Coastguard Worker             return value ? ~0 : 0;
2698*c8dee2aaSAndroid Build Coastguard Worker 
2699*c8dee2aaSAndroid Build Coastguard Worker         default:
2700*c8dee2aaSAndroid Build Coastguard Worker             return std::nullopt;
2701*c8dee2aaSAndroid Build Coastguard Worker     }
2702*c8dee2aaSAndroid Build Coastguard Worker }
2703*c8dee2aaSAndroid Build Coastguard Worker 
getImmutableValueForExpression(const Expression & expr,TArray<ImmutableBits> * immutableValues)2704*c8dee2aaSAndroid Build Coastguard Worker bool Generator::getImmutableValueForExpression(const Expression& expr,
2705*c8dee2aaSAndroid Build Coastguard Worker                                                TArray<ImmutableBits>* immutableValues) {
2706*c8dee2aaSAndroid Build Coastguard Worker     if (!expr.supportsConstantValues()) {
2707*c8dee2aaSAndroid Build Coastguard Worker         return false;
2708*c8dee2aaSAndroid Build Coastguard Worker     }
2709*c8dee2aaSAndroid Build Coastguard Worker     size_t numSlots = expr.type().slotCount();
2710*c8dee2aaSAndroid Build Coastguard Worker     immutableValues->reserve_exact(numSlots);
2711*c8dee2aaSAndroid Build Coastguard Worker     for (size_t index = 0; index < numSlots; ++index) {
2712*c8dee2aaSAndroid Build Coastguard Worker         std::optional<ImmutableBits> bits = this->getImmutableBitsForSlot(expr, index);
2713*c8dee2aaSAndroid Build Coastguard Worker         if (!bits.has_value()) {
2714*c8dee2aaSAndroid Build Coastguard Worker             return false;
2715*c8dee2aaSAndroid Build Coastguard Worker         }
2716*c8dee2aaSAndroid Build Coastguard Worker         immutableValues->push_back(*bits);
2717*c8dee2aaSAndroid Build Coastguard Worker     }
2718*c8dee2aaSAndroid Build Coastguard Worker     return true;
2719*c8dee2aaSAndroid Build Coastguard Worker }
2720*c8dee2aaSAndroid Build Coastguard Worker 
storeImmutableValueToSlots(const TArray<ImmutableBits> & immutableValues,SlotRange slots)2721*c8dee2aaSAndroid Build Coastguard Worker void Generator::storeImmutableValueToSlots(const TArray<ImmutableBits>& immutableValues,
2722*c8dee2aaSAndroid Build Coastguard Worker                                            SlotRange slots) {
2723*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index < slots.count; ++index) {
2724*c8dee2aaSAndroid Build Coastguard Worker         // Store the immutable value in its slot.
2725*c8dee2aaSAndroid Build Coastguard Worker         const Slot slot = slots.index++;
2726*c8dee2aaSAndroid Build Coastguard Worker         const ImmutableBits bits = immutableValues[index];
2727*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.store_immutable_value_i(slot, bits);
2728*c8dee2aaSAndroid Build Coastguard Worker 
2729*c8dee2aaSAndroid Build Coastguard Worker         // Keep track of every stored immutable value for potential later reuse.
2730*c8dee2aaSAndroid Build Coastguard Worker         fImmutableSlotMap[bits].add(slot);
2731*c8dee2aaSAndroid Build Coastguard Worker     }
2732*c8dee2aaSAndroid Build Coastguard Worker }
2733*c8dee2aaSAndroid Build Coastguard Worker 
findPreexistingImmutableData(const TArray<ImmutableBits> & immutableValues)2734*c8dee2aaSAndroid Build Coastguard Worker std::optional<SlotRange> Generator::findPreexistingImmutableData(
2735*c8dee2aaSAndroid Build Coastguard Worker         const TArray<ImmutableBits>& immutableValues) {
2736*c8dee2aaSAndroid Build Coastguard Worker     STArray<16, const THashSet<Slot>*> slotArray;
2737*c8dee2aaSAndroid Build Coastguard Worker     slotArray.reserve_exact(immutableValues.size());
2738*c8dee2aaSAndroid Build Coastguard Worker 
2739*c8dee2aaSAndroid Build Coastguard Worker     // Find all the slots associated with each immutable-value bit representation.
2740*c8dee2aaSAndroid Build Coastguard Worker     // If a given bit-pattern doesn't exist anywhere in our program yet, we can stop searching.
2741*c8dee2aaSAndroid Build Coastguard Worker     for (const ImmutableBits& immutableValue : immutableValues) {
2742*c8dee2aaSAndroid Build Coastguard Worker         const THashSet<Slot>* slotsForValue = fImmutableSlotMap.find(immutableValue);
2743*c8dee2aaSAndroid Build Coastguard Worker         if (!slotsForValue) {
2744*c8dee2aaSAndroid Build Coastguard Worker             return std::nullopt;
2745*c8dee2aaSAndroid Build Coastguard Worker         }
2746*c8dee2aaSAndroid Build Coastguard Worker         slotArray.push_back(slotsForValue);
2747*c8dee2aaSAndroid Build Coastguard Worker     }
2748*c8dee2aaSAndroid Build Coastguard Worker 
2749*c8dee2aaSAndroid Build Coastguard Worker     // Look for the group with the fewest number of entries, since that can be searched in the
2750*c8dee2aaSAndroid Build Coastguard Worker     // least amount of effort.
2751*c8dee2aaSAndroid Build Coastguard Worker     int leastSlotIndex = 0, leastSlotCount = INT_MAX;
2752*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index < slotArray.size(); ++index) {
2753*c8dee2aaSAndroid Build Coastguard Worker         int currentCount = slotArray[index]->count();
2754*c8dee2aaSAndroid Build Coastguard Worker         if (currentCount < leastSlotCount) {
2755*c8dee2aaSAndroid Build Coastguard Worker             leastSlotIndex = index;
2756*c8dee2aaSAndroid Build Coastguard Worker             leastSlotCount = currentCount;
2757*c8dee2aaSAndroid Build Coastguard Worker         }
2758*c8dee2aaSAndroid Build Coastguard Worker     }
2759*c8dee2aaSAndroid Build Coastguard Worker 
2760*c8dee2aaSAndroid Build Coastguard Worker     // See if we can reconstitute the value that we want with any of the data we've already got.
2761*c8dee2aaSAndroid Build Coastguard Worker     for (int slot : *slotArray[leastSlotIndex]) {
2762*c8dee2aaSAndroid Build Coastguard Worker         int firstSlot = slot - leastSlotIndex;
2763*c8dee2aaSAndroid Build Coastguard Worker         bool found = true;
2764*c8dee2aaSAndroid Build Coastguard Worker         for (int index = 0; index < slotArray.size(); ++index) {
2765*c8dee2aaSAndroid Build Coastguard Worker             if (!slotArray[index]->contains(firstSlot + index)) {
2766*c8dee2aaSAndroid Build Coastguard Worker                 found = false;
2767*c8dee2aaSAndroid Build Coastguard Worker                 break;
2768*c8dee2aaSAndroid Build Coastguard Worker             }
2769*c8dee2aaSAndroid Build Coastguard Worker         }
2770*c8dee2aaSAndroid Build Coastguard Worker         if (found) {
2771*c8dee2aaSAndroid Build Coastguard Worker             // We've found an exact match for the input value; return its slot-range.
2772*c8dee2aaSAndroid Build Coastguard Worker             return SlotRange{firstSlot, slotArray.size()};
2773*c8dee2aaSAndroid Build Coastguard Worker         }
2774*c8dee2aaSAndroid Build Coastguard Worker     }
2775*c8dee2aaSAndroid Build Coastguard Worker 
2776*c8dee2aaSAndroid Build Coastguard Worker     // We didn't find any reusable slot ranges.
2777*c8dee2aaSAndroid Build Coastguard Worker     return std::nullopt;
2778*c8dee2aaSAndroid Build Coastguard Worker }
2779*c8dee2aaSAndroid Build Coastguard Worker 
pushImmutableData(const Expression & e)2780*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushImmutableData(const Expression& e) {
2781*c8dee2aaSAndroid Build Coastguard Worker     STArray<16, ImmutableBits> immutableValues;
2782*c8dee2aaSAndroid Build Coastguard Worker     if (!this->getImmutableValueForExpression(e, &immutableValues)) {
2783*c8dee2aaSAndroid Build Coastguard Worker         return false;
2784*c8dee2aaSAndroid Build Coastguard Worker     }
2785*c8dee2aaSAndroid Build Coastguard Worker     std::optional<SlotRange> preexistingData = this->findPreexistingImmutableData(immutableValues);
2786*c8dee2aaSAndroid Build Coastguard Worker     if (preexistingData.has_value()) {
2787*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.push_immutable(*preexistingData);
2788*c8dee2aaSAndroid Build Coastguard Worker         return true;
2789*c8dee2aaSAndroid Build Coastguard Worker     }
2790*c8dee2aaSAndroid Build Coastguard Worker     SlotRange range = fImmutableSlots.createSlots(e.description(),
2791*c8dee2aaSAndroid Build Coastguard Worker                                                   e.type(),
2792*c8dee2aaSAndroid Build Coastguard Worker                                                   e.fPosition,
2793*c8dee2aaSAndroid Build Coastguard Worker                                                   /*isFunctionReturnValue=*/false);
2794*c8dee2aaSAndroid Build Coastguard Worker     this->storeImmutableValueToSlots(immutableValues, range);
2795*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.push_immutable(range);
2796*c8dee2aaSAndroid Build Coastguard Worker     return true;
2797*c8dee2aaSAndroid Build Coastguard Worker }
2798*c8dee2aaSAndroid Build Coastguard Worker 
pushConstructorCompound(const AnyConstructor & c)2799*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushConstructorCompound(const AnyConstructor& c) {
2800*c8dee2aaSAndroid Build Coastguard Worker     if (c.type().slotCount() > 1 && this->pushImmutableData(c)) {
2801*c8dee2aaSAndroid Build Coastguard Worker         return true;
2802*c8dee2aaSAndroid Build Coastguard Worker     }
2803*c8dee2aaSAndroid Build Coastguard Worker     for (const std::unique_ptr<Expression> &arg : c.argumentSpan()) {
2804*c8dee2aaSAndroid Build Coastguard Worker         if (!this->pushExpression(*arg)) {
2805*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
2806*c8dee2aaSAndroid Build Coastguard Worker         }
2807*c8dee2aaSAndroid Build Coastguard Worker     }
2808*c8dee2aaSAndroid Build Coastguard Worker     return true;
2809*c8dee2aaSAndroid Build Coastguard Worker }
2810*c8dee2aaSAndroid Build Coastguard Worker 
pushChildCall(const ChildCall & c)2811*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushChildCall(const ChildCall& c) {
2812*c8dee2aaSAndroid Build Coastguard Worker     int* childIdx = fChildEffectMap.find(&c.child());
2813*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(childIdx != nullptr);
2814*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!c.arguments().empty());
2815*c8dee2aaSAndroid Build Coastguard Worker 
2816*c8dee2aaSAndroid Build Coastguard Worker     // All child calls have at least one argument.
2817*c8dee2aaSAndroid Build Coastguard Worker     const Expression* arg = c.arguments()[0].get();
2818*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushExpression(*arg)) {
2819*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
2820*c8dee2aaSAndroid Build Coastguard Worker     }
2821*c8dee2aaSAndroid Build Coastguard Worker 
2822*c8dee2aaSAndroid Build Coastguard Worker     // Copy arguments from the stack into src/dst as required by this particular child-call.
2823*c8dee2aaSAndroid Build Coastguard Worker     switch (c.child().type().typeKind()) {
2824*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kShader: {
2825*c8dee2aaSAndroid Build Coastguard Worker             // The argument must be a float2.
2826*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(c.arguments().size() == 1);
2827*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg->type().matches(*fContext.fTypes.fFloat2));
2828*c8dee2aaSAndroid Build Coastguard Worker 
2829*c8dee2aaSAndroid Build Coastguard Worker             // `exchange_src` will use the top four values on the stack, but we don't care what goes
2830*c8dee2aaSAndroid Build Coastguard Worker             // into the blue/alpha components. We inject padding here to balance the stack.
2831*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.pad_stack(2);
2832*c8dee2aaSAndroid Build Coastguard Worker 
2833*c8dee2aaSAndroid Build Coastguard Worker             // Move the argument into src.rgba while also preserving the execution mask.
2834*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.exchange_src();
2835*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.invoke_shader(*childIdx);
2836*c8dee2aaSAndroid Build Coastguard Worker             break;
2837*c8dee2aaSAndroid Build Coastguard Worker         }
2838*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kColorFilter: {
2839*c8dee2aaSAndroid Build Coastguard Worker             // The argument must be a half4/float4.
2840*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(c.arguments().size() == 1);
2841*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg->type().matches(*fContext.fTypes.fHalf4) ||
2842*c8dee2aaSAndroid Build Coastguard Worker                      arg->type().matches(*fContext.fTypes.fFloat4));
2843*c8dee2aaSAndroid Build Coastguard Worker 
2844*c8dee2aaSAndroid Build Coastguard Worker             // Move the argument into src.rgba while also preserving the execution mask.
2845*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.exchange_src();
2846*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.invoke_color_filter(*childIdx);
2847*c8dee2aaSAndroid Build Coastguard Worker             break;
2848*c8dee2aaSAndroid Build Coastguard Worker         }
2849*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kBlender: {
2850*c8dee2aaSAndroid Build Coastguard Worker             // Both arguments must be half4/float4.
2851*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(c.arguments().size() == 2);
2852*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(c.arguments()[0]->type().matches(*fContext.fTypes.fHalf4) ||
2853*c8dee2aaSAndroid Build Coastguard Worker                      c.arguments()[0]->type().matches(*fContext.fTypes.fFloat4));
2854*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(c.arguments()[1]->type().matches(*fContext.fTypes.fHalf4) ||
2855*c8dee2aaSAndroid Build Coastguard Worker                      c.arguments()[1]->type().matches(*fContext.fTypes.fFloat4));
2856*c8dee2aaSAndroid Build Coastguard Worker 
2857*c8dee2aaSAndroid Build Coastguard Worker             // Move the second argument into dst.rgba, and the first argument into src.rgba, while
2858*c8dee2aaSAndroid Build Coastguard Worker             // simultaneously preserving the execution mask.
2859*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(*c.arguments()[1])) {
2860*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
2861*c8dee2aaSAndroid Build Coastguard Worker             }
2862*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.pop_dst_rgba();
2863*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.exchange_src();
2864*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.invoke_blender(*childIdx);
2865*c8dee2aaSAndroid Build Coastguard Worker             break;
2866*c8dee2aaSAndroid Build Coastguard Worker         }
2867*c8dee2aaSAndroid Build Coastguard Worker         default: {
2868*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAILF("cannot sample from type '%s'", c.child().type().description().c_str());
2869*c8dee2aaSAndroid Build Coastguard Worker         }
2870*c8dee2aaSAndroid Build Coastguard Worker     }
2871*c8dee2aaSAndroid Build Coastguard Worker 
2872*c8dee2aaSAndroid Build Coastguard Worker     // The child call has returned the result color via src.rgba, and the SkRP execution mask is
2873*c8dee2aaSAndroid Build Coastguard Worker     // on top of the stack. Swapping the two puts the result color on top of the stack, and also
2874*c8dee2aaSAndroid Build Coastguard Worker     // restores our execution masks.
2875*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.exchange_src();
2876*c8dee2aaSAndroid Build Coastguard Worker     return true;
2877*c8dee2aaSAndroid Build Coastguard Worker }
2878*c8dee2aaSAndroid Build Coastguard Worker 
pushConstructorCast(const AnyConstructor & c)2879*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushConstructorCast(const AnyConstructor& c) {
2880*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(c.argumentSpan().size() == 1);
2881*c8dee2aaSAndroid Build Coastguard Worker     const Expression& inner = *c.argumentSpan().front();
2882*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(inner.type().slotCount() == c.type().slotCount());
2883*c8dee2aaSAndroid Build Coastguard Worker 
2884*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushExpression(inner)) {
2885*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
2886*c8dee2aaSAndroid Build Coastguard Worker     }
2887*c8dee2aaSAndroid Build Coastguard Worker     const Type::NumberKind innerKind = inner.type().componentType().numberKind();
2888*c8dee2aaSAndroid Build Coastguard Worker     const Type::NumberKind outerKind = c.type().componentType().numberKind();
2889*c8dee2aaSAndroid Build Coastguard Worker 
2890*c8dee2aaSAndroid Build Coastguard Worker     if (innerKind == outerKind) {
2891*c8dee2aaSAndroid Build Coastguard Worker         // Since we ignore type precision, this cast is effectively a no-op.
2892*c8dee2aaSAndroid Build Coastguard Worker         return true;
2893*c8dee2aaSAndroid Build Coastguard Worker     }
2894*c8dee2aaSAndroid Build Coastguard Worker 
2895*c8dee2aaSAndroid Build Coastguard Worker     switch (innerKind) {
2896*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kSigned:
2897*c8dee2aaSAndroid Build Coastguard Worker             if (outerKind == Type::NumberKind::kUnsigned) {
2898*c8dee2aaSAndroid Build Coastguard Worker                 // Treat uint(int) as a no-op.
2899*c8dee2aaSAndroid Build Coastguard Worker                 return true;
2900*c8dee2aaSAndroid Build Coastguard Worker             }
2901*c8dee2aaSAndroid Build Coastguard Worker             if (outerKind == Type::NumberKind::kFloat) {
2902*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.unary_op(BuilderOp::cast_to_float_from_int, c.type().slotCount());
2903*c8dee2aaSAndroid Build Coastguard Worker                 return true;
2904*c8dee2aaSAndroid Build Coastguard Worker             }
2905*c8dee2aaSAndroid Build Coastguard Worker             break;
2906*c8dee2aaSAndroid Build Coastguard Worker 
2907*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kUnsigned:
2908*c8dee2aaSAndroid Build Coastguard Worker             if (outerKind == Type::NumberKind::kSigned) {
2909*c8dee2aaSAndroid Build Coastguard Worker                 // Treat int(uint) as a no-op.
2910*c8dee2aaSAndroid Build Coastguard Worker                 return true;
2911*c8dee2aaSAndroid Build Coastguard Worker             }
2912*c8dee2aaSAndroid Build Coastguard Worker             if (outerKind == Type::NumberKind::kFloat) {
2913*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.unary_op(BuilderOp::cast_to_float_from_uint, c.type().slotCount());
2914*c8dee2aaSAndroid Build Coastguard Worker                 return true;
2915*c8dee2aaSAndroid Build Coastguard Worker             }
2916*c8dee2aaSAndroid Build Coastguard Worker             break;
2917*c8dee2aaSAndroid Build Coastguard Worker 
2918*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kBoolean:
2919*c8dee2aaSAndroid Build Coastguard Worker             // Converting boolean to int or float can be accomplished via bitwise-and.
2920*c8dee2aaSAndroid Build Coastguard Worker             if (outerKind == Type::NumberKind::kFloat) {
2921*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.push_constant_f(1.0f);
2922*c8dee2aaSAndroid Build Coastguard Worker             } else if (outerKind == Type::NumberKind::kSigned ||
2923*c8dee2aaSAndroid Build Coastguard Worker                        outerKind == Type::NumberKind::kUnsigned) {
2924*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.push_constant_i(1);
2925*c8dee2aaSAndroid Build Coastguard Worker             } else {
2926*c8dee2aaSAndroid Build Coastguard Worker                 SkDEBUGFAILF("unexpected cast from bool to %s", c.type().description().c_str());
2927*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
2928*c8dee2aaSAndroid Build Coastguard Worker             }
2929*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.push_duplicates(c.type().slotCount() - 1);
2930*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.binary_op(BuilderOp::bitwise_and_n_ints, c.type().slotCount());
2931*c8dee2aaSAndroid Build Coastguard Worker             return true;
2932*c8dee2aaSAndroid Build Coastguard Worker 
2933*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kFloat:
2934*c8dee2aaSAndroid Build Coastguard Worker             if (outerKind == Type::NumberKind::kSigned) {
2935*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.unary_op(BuilderOp::cast_to_int_from_float, c.type().slotCount());
2936*c8dee2aaSAndroid Build Coastguard Worker                 return true;
2937*c8dee2aaSAndroid Build Coastguard Worker             }
2938*c8dee2aaSAndroid Build Coastguard Worker             if (outerKind == Type::NumberKind::kUnsigned) {
2939*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.unary_op(BuilderOp::cast_to_uint_from_float, c.type().slotCount());
2940*c8dee2aaSAndroid Build Coastguard Worker                 return true;
2941*c8dee2aaSAndroid Build Coastguard Worker             }
2942*c8dee2aaSAndroid Build Coastguard Worker             break;
2943*c8dee2aaSAndroid Build Coastguard Worker 
2944*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kNonnumeric:
2945*c8dee2aaSAndroid Build Coastguard Worker             break;
2946*c8dee2aaSAndroid Build Coastguard Worker     }
2947*c8dee2aaSAndroid Build Coastguard Worker 
2948*c8dee2aaSAndroid Build Coastguard Worker     if (outerKind == Type::NumberKind::kBoolean) {
2949*c8dee2aaSAndroid Build Coastguard Worker         // Converting int or float to boolean can be accomplished via `notEqual(x, 0)`.
2950*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.push_zeros(c.type().slotCount());
2951*c8dee2aaSAndroid Build Coastguard Worker         return this->binaryOp(inner.type(), kNotEqualOps);
2952*c8dee2aaSAndroid Build Coastguard Worker     }
2953*c8dee2aaSAndroid Build Coastguard Worker 
2954*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGFAILF("unexpected cast from %s to %s",
2955*c8dee2aaSAndroid Build Coastguard Worker                  c.type().description().c_str(), inner.type().description().c_str());
2956*c8dee2aaSAndroid Build Coastguard Worker     return unsupported();
2957*c8dee2aaSAndroid Build Coastguard Worker }
2958*c8dee2aaSAndroid Build Coastguard Worker 
pushConstructorDiagonalMatrix(const ConstructorDiagonalMatrix & c)2959*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushConstructorDiagonalMatrix(const ConstructorDiagonalMatrix& c) {
2960*c8dee2aaSAndroid Build Coastguard Worker     if (this->pushImmutableData(c)) {
2961*c8dee2aaSAndroid Build Coastguard Worker         return true;
2962*c8dee2aaSAndroid Build Coastguard Worker     }
2963*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.push_zeros(1);
2964*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushExpression(*c.argument())) {
2965*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
2966*c8dee2aaSAndroid Build Coastguard Worker     }
2967*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.diagonal_matrix(c.type().columns(), c.type().rows());
2968*c8dee2aaSAndroid Build Coastguard Worker 
2969*c8dee2aaSAndroid Build Coastguard Worker     return true;
2970*c8dee2aaSAndroid Build Coastguard Worker }
2971*c8dee2aaSAndroid Build Coastguard Worker 
pushConstructorMatrixResize(const ConstructorMatrixResize & c)2972*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushConstructorMatrixResize(const ConstructorMatrixResize& c) {
2973*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushExpression(*c.argument())) {
2974*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
2975*c8dee2aaSAndroid Build Coastguard Worker     }
2976*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.matrix_resize(c.argument()->type().columns(),
2977*c8dee2aaSAndroid Build Coastguard Worker                            c.argument()->type().rows(),
2978*c8dee2aaSAndroid Build Coastguard Worker                            c.type().columns(),
2979*c8dee2aaSAndroid Build Coastguard Worker                            c.type().rows());
2980*c8dee2aaSAndroid Build Coastguard Worker     return true;
2981*c8dee2aaSAndroid Build Coastguard Worker }
2982*c8dee2aaSAndroid Build Coastguard Worker 
pushConstructorSplat(const ConstructorSplat & c)2983*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushConstructorSplat(const ConstructorSplat& c) {
2984*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushExpression(*c.argument())) {
2985*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
2986*c8dee2aaSAndroid Build Coastguard Worker     }
2987*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.push_duplicates(c.type().slotCount() - 1);
2988*c8dee2aaSAndroid Build Coastguard Worker     return true;
2989*c8dee2aaSAndroid Build Coastguard Worker }
2990*c8dee2aaSAndroid Build Coastguard Worker 
pushFieldAccess(const FieldAccess & f)2991*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushFieldAccess(const FieldAccess& f) {
2992*c8dee2aaSAndroid Build Coastguard Worker     // If possible, get direct field access via the lvalue.
2993*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<LValue> lvalue = this->makeLValue(f, /*allowScratch=*/true);
2994*c8dee2aaSAndroid Build Coastguard Worker     return lvalue && this->push(*lvalue);
2995*c8dee2aaSAndroid Build Coastguard Worker }
2996*c8dee2aaSAndroid Build Coastguard Worker 
pushFunctionCall(const FunctionCall & c)2997*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushFunctionCall(const FunctionCall& c) {
2998*c8dee2aaSAndroid Build Coastguard Worker     if (c.function().isIntrinsic()) {
2999*c8dee2aaSAndroid Build Coastguard Worker         return this->pushIntrinsic(c);
3000*c8dee2aaSAndroid Build Coastguard Worker     }
3001*c8dee2aaSAndroid Build Coastguard Worker 
3002*c8dee2aaSAndroid Build Coastguard Worker     // Keep track of the current function.
3003*c8dee2aaSAndroid Build Coastguard Worker     const FunctionDefinition* lastFunction = fCurrentFunction;
3004*c8dee2aaSAndroid Build Coastguard Worker     fCurrentFunction = c.function().definition();
3005*c8dee2aaSAndroid Build Coastguard Worker 
3006*c8dee2aaSAndroid Build Coastguard Worker     // Skip over the function body entirely if there are no active lanes.
3007*c8dee2aaSAndroid Build Coastguard Worker     // (If the function call was trivial, it would likely have been inlined in the frontend, so we
3008*c8dee2aaSAndroid Build Coastguard Worker     // assume here that function calls generally represent a significant amount of work.)
3009*c8dee2aaSAndroid Build Coastguard Worker     int skipLabelID = fBuilder.nextLabelID();
3010*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.branch_if_no_lanes_active(skipLabelID);
3011*c8dee2aaSAndroid Build Coastguard Worker 
3012*c8dee2aaSAndroid Build Coastguard Worker     // Emit the function body.
3013*c8dee2aaSAndroid Build Coastguard Worker     std::optional<SlotRange> r = this->writeFunction(c, *fCurrentFunction, c.arguments());
3014*c8dee2aaSAndroid Build Coastguard Worker     if (!r.has_value()) {
3015*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
3016*c8dee2aaSAndroid Build Coastguard Worker     }
3017*c8dee2aaSAndroid Build Coastguard Worker 
3018*c8dee2aaSAndroid Build Coastguard Worker     // If the function uses result slots, move its result from slots onto the stack.
3019*c8dee2aaSAndroid Build Coastguard Worker     if (this->needsFunctionResultSlots(fCurrentFunction)) {
3020*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.push_slots(*r);
3021*c8dee2aaSAndroid Build Coastguard Worker     }
3022*c8dee2aaSAndroid Build Coastguard Worker 
3023*c8dee2aaSAndroid Build Coastguard Worker     // We've returned back to the last function.
3024*c8dee2aaSAndroid Build Coastguard Worker     fCurrentFunction = lastFunction;
3025*c8dee2aaSAndroid Build Coastguard Worker 
3026*c8dee2aaSAndroid Build Coastguard Worker     // Copy the function result from its slots onto the stack.
3027*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.label(skipLabelID);
3028*c8dee2aaSAndroid Build Coastguard Worker     return true;
3029*c8dee2aaSAndroid Build Coastguard Worker }
3030*c8dee2aaSAndroid Build Coastguard Worker 
pushIndexExpression(const IndexExpression & i)3031*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushIndexExpression(const IndexExpression& i) {
3032*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<LValue> lvalue = this->makeLValue(i, /*allowScratch=*/true);
3033*c8dee2aaSAndroid Build Coastguard Worker     return lvalue && this->push(*lvalue);
3034*c8dee2aaSAndroid Build Coastguard Worker }
3035*c8dee2aaSAndroid Build Coastguard Worker 
pushIntrinsic(const FunctionCall & c)3036*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushIntrinsic(const FunctionCall& c) {
3037*c8dee2aaSAndroid Build Coastguard Worker     const ExpressionArray& args = c.arguments();
3038*c8dee2aaSAndroid Build Coastguard Worker     switch (args.size()) {
3039*c8dee2aaSAndroid Build Coastguard Worker         case 1:
3040*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(c.function().intrinsicKind(), *args[0]);
3041*c8dee2aaSAndroid Build Coastguard Worker 
3042*c8dee2aaSAndroid Build Coastguard Worker         case 2:
3043*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(c.function().intrinsicKind(), *args[0], *args[1]);
3044*c8dee2aaSAndroid Build Coastguard Worker 
3045*c8dee2aaSAndroid Build Coastguard Worker         case 3:
3046*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(c.function().intrinsicKind(), *args[0], *args[1], *args[2]);
3047*c8dee2aaSAndroid Build Coastguard Worker 
3048*c8dee2aaSAndroid Build Coastguard Worker         default:
3049*c8dee2aaSAndroid Build Coastguard Worker             break;
3050*c8dee2aaSAndroid Build Coastguard Worker     }
3051*c8dee2aaSAndroid Build Coastguard Worker 
3052*c8dee2aaSAndroid Build Coastguard Worker     return unsupported();
3053*c8dee2aaSAndroid Build Coastguard Worker }
3054*c8dee2aaSAndroid Build Coastguard Worker 
pushLengthIntrinsic(int slotCount)3055*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushLengthIntrinsic(int slotCount) {
3056*c8dee2aaSAndroid Build Coastguard Worker     if (slotCount == 1) {
3057*c8dee2aaSAndroid Build Coastguard Worker         // `length(scalar)` is `sqrt(x^2)`, which is equivalent to `abs(x)`.
3058*c8dee2aaSAndroid Build Coastguard Worker         return this->pushAbsFloatIntrinsic(/*slots=*/1);
3059*c8dee2aaSAndroid Build Coastguard Worker     }
3060*c8dee2aaSAndroid Build Coastguard Worker     // Implement `length(vec)` as `sqrt(dot(x, x))`.
3061*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.push_clone(slotCount);
3062*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.dot_floats(slotCount);
3063*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.unary_op(BuilderOp::sqrt_float, 1);
3064*c8dee2aaSAndroid Build Coastguard Worker     return true;
3065*c8dee2aaSAndroid Build Coastguard Worker }
3066*c8dee2aaSAndroid Build Coastguard Worker 
pushAbsFloatIntrinsic(int slots)3067*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushAbsFloatIntrinsic(int slots) {
3068*c8dee2aaSAndroid Build Coastguard Worker     // Perform abs(float) by masking off the sign bit.
3069*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.push_constant_u(0x7FFFFFFF, slots);
3070*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.binary_op(BuilderOp::bitwise_and_n_ints, slots);
3071*c8dee2aaSAndroid Build Coastguard Worker     return true;
3072*c8dee2aaSAndroid Build Coastguard Worker }
3073*c8dee2aaSAndroid Build Coastguard Worker 
pushVectorizedExpression(const Expression & expr,const Type & vectorType)3074*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushVectorizedExpression(const Expression& expr, const Type& vectorType) {
3075*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushExpression(expr)) {
3076*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
3077*c8dee2aaSAndroid Build Coastguard Worker     }
3078*c8dee2aaSAndroid Build Coastguard Worker     if (vectorType.slotCount() > expr.type().slotCount()) {
3079*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(expr.type().slotCount() == 1);
3080*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.push_duplicates(vectorType.slotCount() - expr.type().slotCount());
3081*c8dee2aaSAndroid Build Coastguard Worker     }
3082*c8dee2aaSAndroid Build Coastguard Worker     return true;
3083*c8dee2aaSAndroid Build Coastguard Worker }
3084*c8dee2aaSAndroid Build Coastguard Worker 
pushIntrinsic(const TypedOps & ops,const Expression & arg0)3085*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushIntrinsic(const TypedOps& ops, const Expression& arg0) {
3086*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushExpression(arg0)) {
3087*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
3088*c8dee2aaSAndroid Build Coastguard Worker     }
3089*c8dee2aaSAndroid Build Coastguard Worker     return this->unaryOp(arg0.type(), ops);
3090*c8dee2aaSAndroid Build Coastguard Worker }
3091*c8dee2aaSAndroid Build Coastguard Worker 
pushIntrinsic(BuilderOp builderOp,const Expression & arg0)3092*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushIntrinsic(BuilderOp builderOp, const Expression& arg0) {
3093*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushExpression(arg0)) {
3094*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
3095*c8dee2aaSAndroid Build Coastguard Worker     }
3096*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.unary_op(builderOp, arg0.type().slotCount());
3097*c8dee2aaSAndroid Build Coastguard Worker     return true;
3098*c8dee2aaSAndroid Build Coastguard Worker }
3099*c8dee2aaSAndroid Build Coastguard Worker 
pushIntrinsic(IntrinsicKind intrinsic,const Expression & arg0)3100*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushIntrinsic(IntrinsicKind intrinsic, const Expression& arg0) {
3101*c8dee2aaSAndroid Build Coastguard Worker     switch (intrinsic) {
3102*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_abs_IntrinsicKind:
3103*c8dee2aaSAndroid Build Coastguard Worker             if (arg0.type().componentType().isFloat()) {
3104*c8dee2aaSAndroid Build Coastguard Worker                 // Perform abs(float) by masking off the sign bit.
3105*c8dee2aaSAndroid Build Coastguard Worker                 if (!this->pushExpression(arg0)) {
3106*c8dee2aaSAndroid Build Coastguard Worker                     return unsupported();
3107*c8dee2aaSAndroid Build Coastguard Worker                 }
3108*c8dee2aaSAndroid Build Coastguard Worker                 return this->pushAbsFloatIntrinsic(arg0.type().slotCount());
3109*c8dee2aaSAndroid Build Coastguard Worker             }
3110*c8dee2aaSAndroid Build Coastguard Worker             // We have a dedicated op for abs(int).
3111*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(BuilderOp::abs_int, arg0);
3112*c8dee2aaSAndroid Build Coastguard Worker 
3113*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_any_IntrinsicKind:
3114*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(arg0)) {
3115*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3116*c8dee2aaSAndroid Build Coastguard Worker             }
3117*c8dee2aaSAndroid Build Coastguard Worker             this->foldWithMultiOp(BuilderOp::bitwise_or_n_ints, arg0.type().slotCount());
3118*c8dee2aaSAndroid Build Coastguard Worker             return true;
3119*c8dee2aaSAndroid Build Coastguard Worker 
3120*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_all_IntrinsicKind:
3121*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(arg0)) {
3122*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3123*c8dee2aaSAndroid Build Coastguard Worker             }
3124*c8dee2aaSAndroid Build Coastguard Worker             this->foldWithMultiOp(BuilderOp::bitwise_and_n_ints, arg0.type().slotCount());
3125*c8dee2aaSAndroid Build Coastguard Worker             return true;
3126*c8dee2aaSAndroid Build Coastguard Worker 
3127*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_acos_IntrinsicKind:
3128*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(BuilderOp::acos_float, arg0);
3129*c8dee2aaSAndroid Build Coastguard Worker 
3130*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_asin_IntrinsicKind:
3131*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(BuilderOp::asin_float, arg0);
3132*c8dee2aaSAndroid Build Coastguard Worker 
3133*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_atan_IntrinsicKind:
3134*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(BuilderOp::atan_float, arg0);
3135*c8dee2aaSAndroid Build Coastguard Worker 
3136*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_ceil_IntrinsicKind:
3137*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(BuilderOp::ceil_float, arg0);
3138*c8dee2aaSAndroid Build Coastguard Worker 
3139*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_cos_IntrinsicKind:
3140*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(BuilderOp::cos_float, arg0);
3141*c8dee2aaSAndroid Build Coastguard Worker 
3142*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_degrees_IntrinsicKind: {
3143*c8dee2aaSAndroid Build Coastguard Worker             Literal lit180OverPi{Position{}, 57.2957795131f, &arg0.type().componentType()};
3144*c8dee2aaSAndroid Build Coastguard Worker             return this->pushBinaryExpression(arg0, OperatorKind::STAR, lit180OverPi);
3145*c8dee2aaSAndroid Build Coastguard Worker         }
3146*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_floatBitsToInt_IntrinsicKind:
3147*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_floatBitsToUint_IntrinsicKind:
3148*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_intBitsToFloat_IntrinsicKind:
3149*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_uintBitsToFloat_IntrinsicKind:
3150*c8dee2aaSAndroid Build Coastguard Worker             return this->pushExpression(arg0);
3151*c8dee2aaSAndroid Build Coastguard Worker 
3152*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_exp_IntrinsicKind:
3153*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(BuilderOp::exp_float, arg0);
3154*c8dee2aaSAndroid Build Coastguard Worker 
3155*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_exp2_IntrinsicKind:
3156*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(BuilderOp::exp2_float, arg0);
3157*c8dee2aaSAndroid Build Coastguard Worker 
3158*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_floor_IntrinsicKind:
3159*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(BuilderOp::floor_float, arg0);
3160*c8dee2aaSAndroid Build Coastguard Worker 
3161*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_fract_IntrinsicKind:
3162*c8dee2aaSAndroid Build Coastguard Worker             // Implement fract as `x - floor(x)`.
3163*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(arg0)) {
3164*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3165*c8dee2aaSAndroid Build Coastguard Worker             }
3166*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.push_clone(arg0.type().slotCount());
3167*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.unary_op(BuilderOp::floor_float, arg0.type().slotCount());
3168*c8dee2aaSAndroid Build Coastguard Worker             return this->binaryOp(arg0.type(), kSubtractOps);
3169*c8dee2aaSAndroid Build Coastguard Worker 
3170*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_inverse_IntrinsicKind:
3171*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().isMatrix());
3172*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().rows() == arg0.type().columns());
3173*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(arg0)) {
3174*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3175*c8dee2aaSAndroid Build Coastguard Worker             }
3176*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.inverse_matrix(arg0.type().rows());
3177*c8dee2aaSAndroid Build Coastguard Worker             return true;
3178*c8dee2aaSAndroid Build Coastguard Worker 
3179*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_inversesqrt_IntrinsicKind:
3180*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(kInverseSqrtOps, arg0);
3181*c8dee2aaSAndroid Build Coastguard Worker 
3182*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_length_IntrinsicKind:
3183*c8dee2aaSAndroid Build Coastguard Worker             return this->pushExpression(arg0) &&
3184*c8dee2aaSAndroid Build Coastguard Worker                    this->pushLengthIntrinsic(arg0.type().slotCount());
3185*c8dee2aaSAndroid Build Coastguard Worker 
3186*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_log_IntrinsicKind:
3187*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(arg0)) {
3188*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3189*c8dee2aaSAndroid Build Coastguard Worker             }
3190*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.unary_op(BuilderOp::log_float, arg0.type().slotCount());
3191*c8dee2aaSAndroid Build Coastguard Worker             return true;
3192*c8dee2aaSAndroid Build Coastguard Worker 
3193*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_log2_IntrinsicKind:
3194*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(arg0)) {
3195*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3196*c8dee2aaSAndroid Build Coastguard Worker             }
3197*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.unary_op(BuilderOp::log2_float, arg0.type().slotCount());
3198*c8dee2aaSAndroid Build Coastguard Worker             return true;
3199*c8dee2aaSAndroid Build Coastguard Worker 
3200*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_normalize_IntrinsicKind: {
3201*c8dee2aaSAndroid Build Coastguard Worker             // Implement normalize as `x / length(x)`. First, push the expression.
3202*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(arg0)) {
3203*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3204*c8dee2aaSAndroid Build Coastguard Worker             }
3205*c8dee2aaSAndroid Build Coastguard Worker             int slotCount = arg0.type().slotCount();
3206*c8dee2aaSAndroid Build Coastguard Worker             if (slotCount > 1) {
3207*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_USE_RSQRT_IN_RP_NORMALIZE)
3208*c8dee2aaSAndroid Build Coastguard Worker                 // Instead of `x / sqrt(dot(x, x))`, we can get roughly the same result in less time
3209*c8dee2aaSAndroid Build Coastguard Worker                 // by computing `x * invsqrt(dot(x, x))`.
3210*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.push_clone(slotCount);
3211*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.push_clone(slotCount);
3212*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.dot_floats(slotCount);
3213*c8dee2aaSAndroid Build Coastguard Worker 
3214*c8dee2aaSAndroid Build Coastguard Worker                 // Compute `vec(inversesqrt(dot(x, x)))`.
3215*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.unary_op(BuilderOp::invsqrt_float, 1);
3216*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.push_duplicates(slotCount - 1);
3217*c8dee2aaSAndroid Build Coastguard Worker 
3218*c8dee2aaSAndroid Build Coastguard Worker                 // Return `x * vec(inversesqrt(dot(x, x)))`.
3219*c8dee2aaSAndroid Build Coastguard Worker                 return this->binaryOp(arg0.type(), kMultiplyOps);
3220*c8dee2aaSAndroid Build Coastguard Worker #else
3221*c8dee2aaSAndroid Build Coastguard Worker                 // TODO: We can get roughly the same result in less time by using `invsqrt`, but
3222*c8dee2aaSAndroid Build Coastguard Worker                 // that leads to more variance across architectures, which Chromium layout tests do
3223*c8dee2aaSAndroid Build Coastguard Worker                 // not handle nicely.
3224*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.push_clone(slotCount);
3225*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.push_clone(slotCount);
3226*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.dot_floats(slotCount);
3227*c8dee2aaSAndroid Build Coastguard Worker 
3228*c8dee2aaSAndroid Build Coastguard Worker                 // Compute `vec(sqrt(dot(x, x)))`.
3229*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.unary_op(BuilderOp::sqrt_float, 1);
3230*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.push_duplicates(slotCount - 1);
3231*c8dee2aaSAndroid Build Coastguard Worker 
3232*c8dee2aaSAndroid Build Coastguard Worker                 // Return `x / vec(sqrt(dot(x, x)))`.
3233*c8dee2aaSAndroid Build Coastguard Worker                 return this->binaryOp(arg0.type(), kDivideOps);
3234*c8dee2aaSAndroid Build Coastguard Worker #endif
3235*c8dee2aaSAndroid Build Coastguard Worker             } else {
3236*c8dee2aaSAndroid Build Coastguard Worker                 // For single-slot normalization, we can simplify `sqrt(x * x)` into `abs(x)`.
3237*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.push_clone(slotCount);
3238*c8dee2aaSAndroid Build Coastguard Worker                 return this->pushAbsFloatIntrinsic(/*slots=*/1) &&
3239*c8dee2aaSAndroid Build Coastguard Worker                        this->binaryOp(arg0.type(), kDivideOps);
3240*c8dee2aaSAndroid Build Coastguard Worker             }
3241*c8dee2aaSAndroid Build Coastguard Worker         }
3242*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_not_IntrinsicKind:
3243*c8dee2aaSAndroid Build Coastguard Worker             return this->pushPrefixExpression(OperatorKind::LOGICALNOT, arg0);
3244*c8dee2aaSAndroid Build Coastguard Worker 
3245*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_radians_IntrinsicKind: {
3246*c8dee2aaSAndroid Build Coastguard Worker             Literal litPiOver180{Position{}, 0.01745329251f, &arg0.type().componentType()};
3247*c8dee2aaSAndroid Build Coastguard Worker             return this->pushBinaryExpression(arg0, OperatorKind::STAR, litPiOver180);
3248*c8dee2aaSAndroid Build Coastguard Worker         }
3249*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_saturate_IntrinsicKind: {
3250*c8dee2aaSAndroid Build Coastguard Worker             // Implement saturate as clamp(arg, 0, 1).
3251*c8dee2aaSAndroid Build Coastguard Worker             Literal zeroLiteral{Position{}, 0.0, &arg0.type().componentType()};
3252*c8dee2aaSAndroid Build Coastguard Worker             Literal oneLiteral{Position{}, 1.0, &arg0.type().componentType()};
3253*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(k_clamp_IntrinsicKind, arg0, zeroLiteral, oneLiteral);
3254*c8dee2aaSAndroid Build Coastguard Worker         }
3255*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_sign_IntrinsicKind: {
3256*c8dee2aaSAndroid Build Coastguard Worker             // Implement floating-point sign() as `clamp(arg * FLT_MAX, -1, 1)`.
3257*c8dee2aaSAndroid Build Coastguard Worker             // FLT_MIN * FLT_MAX evaluates to 4, so multiplying any float value against FLT_MAX is
3258*c8dee2aaSAndroid Build Coastguard Worker             // sufficient to ensure that |value| is always 1 or greater (excluding zero and nan).
3259*c8dee2aaSAndroid Build Coastguard Worker             // Integer sign() doesn't need to worry about fractional values or nans, and can simply
3260*c8dee2aaSAndroid Build Coastguard Worker             // be `clamp(arg, -1, 1)`.
3261*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(arg0)) {
3262*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3263*c8dee2aaSAndroid Build Coastguard Worker             }
3264*c8dee2aaSAndroid Build Coastguard Worker             if (arg0.type().componentType().isFloat()) {
3265*c8dee2aaSAndroid Build Coastguard Worker                 Literal fltMaxLiteral{Position{}, FLT_MAX, &arg0.type().componentType()};
3266*c8dee2aaSAndroid Build Coastguard Worker                 if (!this->pushVectorizedExpression(fltMaxLiteral, arg0.type())) {
3267*c8dee2aaSAndroid Build Coastguard Worker                     return unsupported();
3268*c8dee2aaSAndroid Build Coastguard Worker                 }
3269*c8dee2aaSAndroid Build Coastguard Worker                 if (!this->binaryOp(arg0.type(), kMultiplyOps)) {
3270*c8dee2aaSAndroid Build Coastguard Worker                     return unsupported();
3271*c8dee2aaSAndroid Build Coastguard Worker                 }
3272*c8dee2aaSAndroid Build Coastguard Worker             }
3273*c8dee2aaSAndroid Build Coastguard Worker             Literal neg1Literal{Position{}, -1.0, &arg0.type().componentType()};
3274*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushVectorizedExpression(neg1Literal, arg0.type())) {
3275*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3276*c8dee2aaSAndroid Build Coastguard Worker             }
3277*c8dee2aaSAndroid Build Coastguard Worker             if (!this->binaryOp(arg0.type(), kMaxOps)) {
3278*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3279*c8dee2aaSAndroid Build Coastguard Worker             }
3280*c8dee2aaSAndroid Build Coastguard Worker             Literal pos1Literal{Position{}, 1.0, &arg0.type().componentType()};
3281*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushVectorizedExpression(pos1Literal, arg0.type())) {
3282*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3283*c8dee2aaSAndroid Build Coastguard Worker             }
3284*c8dee2aaSAndroid Build Coastguard Worker             return this->binaryOp(arg0.type(), kMinOps);
3285*c8dee2aaSAndroid Build Coastguard Worker         }
3286*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_sin_IntrinsicKind:
3287*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(BuilderOp::sin_float, arg0);
3288*c8dee2aaSAndroid Build Coastguard Worker 
3289*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_sqrt_IntrinsicKind:
3290*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(BuilderOp::sqrt_float, arg0);
3291*c8dee2aaSAndroid Build Coastguard Worker 
3292*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_tan_IntrinsicKind:
3293*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(BuilderOp::tan_float, arg0);
3294*c8dee2aaSAndroid Build Coastguard Worker 
3295*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_transpose_IntrinsicKind:
3296*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().isMatrix());
3297*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(arg0)) {
3298*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3299*c8dee2aaSAndroid Build Coastguard Worker             }
3300*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.transpose(arg0.type().columns(), arg0.type().rows());
3301*c8dee2aaSAndroid Build Coastguard Worker             return true;
3302*c8dee2aaSAndroid Build Coastguard Worker 
3303*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_trunc_IntrinsicKind:
3304*c8dee2aaSAndroid Build Coastguard Worker             // Implement trunc as `float(int(x))`, since float-to-int rounds toward zero.
3305*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(arg0)) {
3306*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3307*c8dee2aaSAndroid Build Coastguard Worker             }
3308*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.unary_op(BuilderOp::cast_to_int_from_float, arg0.type().slotCount());
3309*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.unary_op(BuilderOp::cast_to_float_from_int, arg0.type().slotCount());
3310*c8dee2aaSAndroid Build Coastguard Worker             return true;
3311*c8dee2aaSAndroid Build Coastguard Worker 
3312*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_fromLinearSrgb_IntrinsicKind:
3313*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_toLinearSrgb_IntrinsicKind:
3314*c8dee2aaSAndroid Build Coastguard Worker             // The argument must be a half3.
3315*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().matches(*fContext.fTypes.fHalf3));
3316*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(arg0)) {
3317*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3318*c8dee2aaSAndroid Build Coastguard Worker             }
3319*c8dee2aaSAndroid Build Coastguard Worker 
3320*c8dee2aaSAndroid Build Coastguard Worker             if (intrinsic == IntrinsicKind::k_fromLinearSrgb_IntrinsicKind) {
3321*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.invoke_from_linear_srgb();
3322*c8dee2aaSAndroid Build Coastguard Worker             } else {
3323*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.invoke_to_linear_srgb();
3324*c8dee2aaSAndroid Build Coastguard Worker             }
3325*c8dee2aaSAndroid Build Coastguard Worker             return true;
3326*c8dee2aaSAndroid Build Coastguard Worker 
3327*c8dee2aaSAndroid Build Coastguard Worker         default:
3328*c8dee2aaSAndroid Build Coastguard Worker             break;
3329*c8dee2aaSAndroid Build Coastguard Worker     }
3330*c8dee2aaSAndroid Build Coastguard Worker     return unsupported();
3331*c8dee2aaSAndroid Build Coastguard Worker }
3332*c8dee2aaSAndroid Build Coastguard Worker 
pushIntrinsic(const TypedOps & ops,const Expression & arg0,const Expression & arg1)3333*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushIntrinsic(const TypedOps& ops, const Expression& arg0, const Expression& arg1) {
3334*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushExpression(arg0) || !this->pushVectorizedExpression(arg1, arg0.type())) {
3335*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
3336*c8dee2aaSAndroid Build Coastguard Worker     }
3337*c8dee2aaSAndroid Build Coastguard Worker     return this->binaryOp(arg0.type(), ops);
3338*c8dee2aaSAndroid Build Coastguard Worker }
3339*c8dee2aaSAndroid Build Coastguard Worker 
pushIntrinsic(BuilderOp builderOp,const Expression & arg0,const Expression & arg1)3340*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushIntrinsic(BuilderOp builderOp, const Expression& arg0, const Expression& arg1) {
3341*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushExpression(arg0) || !this->pushVectorizedExpression(arg1, arg0.type())) {
3342*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
3343*c8dee2aaSAndroid Build Coastguard Worker     }
3344*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.binary_op(builderOp, arg0.type().slotCount());
3345*c8dee2aaSAndroid Build Coastguard Worker     return true;
3346*c8dee2aaSAndroid Build Coastguard Worker }
3347*c8dee2aaSAndroid Build Coastguard Worker 
pushIntrinsic(IntrinsicKind intrinsic,const Expression & arg0,const Expression & arg1)3348*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushIntrinsic(IntrinsicKind intrinsic,
3349*c8dee2aaSAndroid Build Coastguard Worker                               const Expression& arg0,
3350*c8dee2aaSAndroid Build Coastguard Worker                               const Expression& arg1) {
3351*c8dee2aaSAndroid Build Coastguard Worker     switch (intrinsic) {
3352*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_atan_IntrinsicKind:
3353*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(BuilderOp::atan2_n_floats, arg0, arg1);
3354*c8dee2aaSAndroid Build Coastguard Worker 
3355*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_cross_IntrinsicKind: {
3356*c8dee2aaSAndroid Build Coastguard Worker             // Implement cross as `arg0.yzx * arg1.zxy - arg0.zxy * arg1.yzx`. We use two stacks so
3357*c8dee2aaSAndroid Build Coastguard Worker             // that each subexpression can be multiplied separately.
3358*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().matches(arg1.type()));
3359*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().slotCount() == 3);
3360*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg1.type().slotCount() == 3);
3361*c8dee2aaSAndroid Build Coastguard Worker 
3362*c8dee2aaSAndroid Build Coastguard Worker             // Push `arg0.yzx` onto this stack and `arg0.zxy` onto a separate subexpression stack.
3363*c8dee2aaSAndroid Build Coastguard Worker             AutoStack subexpressionStack(this);
3364*c8dee2aaSAndroid Build Coastguard Worker             subexpressionStack.enter();
3365*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(arg0)) {
3366*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3367*c8dee2aaSAndroid Build Coastguard Worker             }
3368*c8dee2aaSAndroid Build Coastguard Worker             subexpressionStack.exit();
3369*c8dee2aaSAndroid Build Coastguard Worker             subexpressionStack.pushClone(/*slots=*/3);
3370*c8dee2aaSAndroid Build Coastguard Worker 
3371*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.swizzle(/*consumedSlots=*/3, {1, 2, 0});
3372*c8dee2aaSAndroid Build Coastguard Worker             subexpressionStack.enter();
3373*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.swizzle(/*consumedSlots=*/3, {2, 0, 1});
3374*c8dee2aaSAndroid Build Coastguard Worker             subexpressionStack.exit();
3375*c8dee2aaSAndroid Build Coastguard Worker 
3376*c8dee2aaSAndroid Build Coastguard Worker             // Push `arg1.zxy` onto this stack and `arg1.yzx` onto the next stack. Perform the
3377*c8dee2aaSAndroid Build Coastguard Worker             // multiply on each subexpression (`arg0.yzx * arg1.zxy` on the first stack, and
3378*c8dee2aaSAndroid Build Coastguard Worker             // `arg0.zxy * arg1.yzx` on the next).
3379*c8dee2aaSAndroid Build Coastguard Worker             subexpressionStack.enter();
3380*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(arg1)) {
3381*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3382*c8dee2aaSAndroid Build Coastguard Worker             }
3383*c8dee2aaSAndroid Build Coastguard Worker             subexpressionStack.exit();
3384*c8dee2aaSAndroid Build Coastguard Worker             subexpressionStack.pushClone(/*slots=*/3);
3385*c8dee2aaSAndroid Build Coastguard Worker 
3386*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.swizzle(/*consumedSlots=*/3, {2, 0, 1});
3387*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.binary_op(BuilderOp::mul_n_floats, 3);
3388*c8dee2aaSAndroid Build Coastguard Worker 
3389*c8dee2aaSAndroid Build Coastguard Worker             subexpressionStack.enter();
3390*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.swizzle(/*consumedSlots=*/3, {1, 2, 0});
3391*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.binary_op(BuilderOp::mul_n_floats, 3);
3392*c8dee2aaSAndroid Build Coastguard Worker             subexpressionStack.exit();
3393*c8dee2aaSAndroid Build Coastguard Worker 
3394*c8dee2aaSAndroid Build Coastguard Worker             // Migrate the result of the second subexpression (`arg0.zxy * arg1.yzx`) back onto the
3395*c8dee2aaSAndroid Build Coastguard Worker             // main stack and subtract it from the first subexpression (`arg0.yzx * arg1.zxy`).
3396*c8dee2aaSAndroid Build Coastguard Worker             subexpressionStack.pushClone(/*slots=*/3);
3397*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.binary_op(BuilderOp::sub_n_floats, 3);
3398*c8dee2aaSAndroid Build Coastguard Worker 
3399*c8dee2aaSAndroid Build Coastguard Worker             // Now that the calculation is complete, discard the subexpression on the next stack.
3400*c8dee2aaSAndroid Build Coastguard Worker             subexpressionStack.enter();
3401*c8dee2aaSAndroid Build Coastguard Worker             this->discardExpression(/*slots=*/3);
3402*c8dee2aaSAndroid Build Coastguard Worker             subexpressionStack.exit();
3403*c8dee2aaSAndroid Build Coastguard Worker             return true;
3404*c8dee2aaSAndroid Build Coastguard Worker         }
3405*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_distance_IntrinsicKind:
3406*c8dee2aaSAndroid Build Coastguard Worker             // Implement distance as `length(a - b)`.
3407*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().slotCount() == arg1.type().slotCount());
3408*c8dee2aaSAndroid Build Coastguard Worker             return this->pushBinaryExpression(arg0, OperatorKind::MINUS, arg1) &&
3409*c8dee2aaSAndroid Build Coastguard Worker                    this->pushLengthIntrinsic(arg0.type().slotCount());
3410*c8dee2aaSAndroid Build Coastguard Worker 
3411*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_dot_IntrinsicKind:
3412*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().matches(arg1.type()));
3413*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(arg0) || !this->pushExpression(arg1)) {
3414*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3415*c8dee2aaSAndroid Build Coastguard Worker             }
3416*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.dot_floats(arg0.type().slotCount());
3417*c8dee2aaSAndroid Build Coastguard Worker             return true;
3418*c8dee2aaSAndroid Build Coastguard Worker 
3419*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_equal_IntrinsicKind:
3420*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().matches(arg1.type()));
3421*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(kEqualOps, arg0, arg1);
3422*c8dee2aaSAndroid Build Coastguard Worker 
3423*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_notEqual_IntrinsicKind:
3424*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().matches(arg1.type()));
3425*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(kNotEqualOps, arg0, arg1);
3426*c8dee2aaSAndroid Build Coastguard Worker 
3427*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_lessThan_IntrinsicKind:
3428*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().matches(arg1.type()));
3429*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(kLessThanOps, arg0, arg1);
3430*c8dee2aaSAndroid Build Coastguard Worker 
3431*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_greaterThan_IntrinsicKind:
3432*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().matches(arg1.type()));
3433*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(kLessThanOps, arg1, arg0);
3434*c8dee2aaSAndroid Build Coastguard Worker 
3435*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_lessThanEqual_IntrinsicKind:
3436*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().matches(arg1.type()));
3437*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(kLessThanEqualOps, arg0, arg1);
3438*c8dee2aaSAndroid Build Coastguard Worker 
3439*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_greaterThanEqual_IntrinsicKind:
3440*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().matches(arg1.type()));
3441*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(kLessThanEqualOps, arg1, arg0);
3442*c8dee2aaSAndroid Build Coastguard Worker 
3443*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_min_IntrinsicKind:
3444*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().componentType().matches(arg1.type().componentType()));
3445*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(kMinOps, arg0, arg1);
3446*c8dee2aaSAndroid Build Coastguard Worker 
3447*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_matrixCompMult_IntrinsicKind:
3448*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().matches(arg1.type()));
3449*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(kMultiplyOps, arg0, arg1);
3450*c8dee2aaSAndroid Build Coastguard Worker 
3451*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_max_IntrinsicKind:
3452*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().componentType().matches(arg1.type().componentType()));
3453*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(kMaxOps, arg0, arg1);
3454*c8dee2aaSAndroid Build Coastguard Worker 
3455*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_mod_IntrinsicKind:
3456*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().componentType().matches(arg1.type().componentType()));
3457*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(kModOps, arg0, arg1);
3458*c8dee2aaSAndroid Build Coastguard Worker 
3459*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_pow_IntrinsicKind:
3460*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().matches(arg1.type()));
3461*c8dee2aaSAndroid Build Coastguard Worker             return this->pushIntrinsic(BuilderOp::pow_n_floats, arg0, arg1);
3462*c8dee2aaSAndroid Build Coastguard Worker 
3463*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_reflect_IntrinsicKind: {
3464*c8dee2aaSAndroid Build Coastguard Worker             // Implement reflect as `I - (N * dot(I,N) * 2)`.
3465*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().matches(arg1.type()));
3466*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().slotCount() == arg1.type().slotCount());
3467*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().componentType().isFloat());
3468*c8dee2aaSAndroid Build Coastguard Worker             int slotCount = arg0.type().slotCount();
3469*c8dee2aaSAndroid Build Coastguard Worker 
3470*c8dee2aaSAndroid Build Coastguard Worker             // Stack: I, N.
3471*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(arg0) || !this->pushExpression(arg1)) {
3472*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3473*c8dee2aaSAndroid Build Coastguard Worker             }
3474*c8dee2aaSAndroid Build Coastguard Worker             // Stack: I, N, I, N.
3475*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.push_clone(2 * slotCount);
3476*c8dee2aaSAndroid Build Coastguard Worker             // Stack: I, N, dot(I,N)
3477*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.dot_floats(slotCount);
3478*c8dee2aaSAndroid Build Coastguard Worker             // Stack: I, N, dot(I,N), 2
3479*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.push_constant_f(2.0);
3480*c8dee2aaSAndroid Build Coastguard Worker             // Stack: I, N, dot(I,N) * 2
3481*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.binary_op(BuilderOp::mul_n_floats, 1);
3482*c8dee2aaSAndroid Build Coastguard Worker             // Stack: I, N * dot(I,N) * 2
3483*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.push_duplicates(slotCount - 1);
3484*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.binary_op(BuilderOp::mul_n_floats, slotCount);
3485*c8dee2aaSAndroid Build Coastguard Worker             // Stack: I - (N * dot(I,N) * 2)
3486*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.binary_op(BuilderOp::sub_n_floats, slotCount);
3487*c8dee2aaSAndroid Build Coastguard Worker             return true;
3488*c8dee2aaSAndroid Build Coastguard Worker         }
3489*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_step_IntrinsicKind: {
3490*c8dee2aaSAndroid Build Coastguard Worker             // Compute step as `float(lessThanEqual(edge, x))`. We convert from boolean 0/~0 to
3491*c8dee2aaSAndroid Build Coastguard Worker             // floating point zero/one by using a bitwise-and against the bit-pattern of 1.0.
3492*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().componentType().matches(arg1.type().componentType()));
3493*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushVectorizedExpression(arg0, arg1.type()) || !this->pushExpression(arg1)) {
3494*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3495*c8dee2aaSAndroid Build Coastguard Worker             }
3496*c8dee2aaSAndroid Build Coastguard Worker             if (!this->binaryOp(arg1.type(), kLessThanEqualOps)) {
3497*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3498*c8dee2aaSAndroid Build Coastguard Worker             }
3499*c8dee2aaSAndroid Build Coastguard Worker             Literal pos1Literal{Position{}, 1.0, &arg1.type().componentType()};
3500*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushVectorizedExpression(pos1Literal, arg1.type())) {
3501*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3502*c8dee2aaSAndroid Build Coastguard Worker             }
3503*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.binary_op(BuilderOp::bitwise_and_n_ints, arg1.type().slotCount());
3504*c8dee2aaSAndroid Build Coastguard Worker             return true;
3505*c8dee2aaSAndroid Build Coastguard Worker         }
3506*c8dee2aaSAndroid Build Coastguard Worker 
3507*c8dee2aaSAndroid Build Coastguard Worker         default:
3508*c8dee2aaSAndroid Build Coastguard Worker             break;
3509*c8dee2aaSAndroid Build Coastguard Worker     }
3510*c8dee2aaSAndroid Build Coastguard Worker     return unsupported();
3511*c8dee2aaSAndroid Build Coastguard Worker }
3512*c8dee2aaSAndroid Build Coastguard Worker 
pushIntrinsic(IntrinsicKind intrinsic,const Expression & arg0,const Expression & arg1,const Expression & arg2)3513*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushIntrinsic(IntrinsicKind intrinsic,
3514*c8dee2aaSAndroid Build Coastguard Worker                               const Expression& arg0,
3515*c8dee2aaSAndroid Build Coastguard Worker                               const Expression& arg1,
3516*c8dee2aaSAndroid Build Coastguard Worker                               const Expression& arg2) {
3517*c8dee2aaSAndroid Build Coastguard Worker     switch (intrinsic) {
3518*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_clamp_IntrinsicKind:
3519*c8dee2aaSAndroid Build Coastguard Worker             // Implement clamp as min(max(arg, low), high).
3520*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().componentType().matches(arg1.type().componentType()));
3521*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().componentType().matches(arg2.type().componentType()));
3522*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(arg0) || !this->pushVectorizedExpression(arg1, arg0.type())) {
3523*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3524*c8dee2aaSAndroid Build Coastguard Worker             }
3525*c8dee2aaSAndroid Build Coastguard Worker             if (!this->binaryOp(arg0.type(), kMaxOps)) {
3526*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3527*c8dee2aaSAndroid Build Coastguard Worker             }
3528*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushVectorizedExpression(arg2, arg0.type())) {
3529*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3530*c8dee2aaSAndroid Build Coastguard Worker             }
3531*c8dee2aaSAndroid Build Coastguard Worker             if (!this->binaryOp(arg0.type(), kMinOps)) {
3532*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3533*c8dee2aaSAndroid Build Coastguard Worker             }
3534*c8dee2aaSAndroid Build Coastguard Worker             return true;
3535*c8dee2aaSAndroid Build Coastguard Worker 
3536*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_faceforward_IntrinsicKind: {
3537*c8dee2aaSAndroid Build Coastguard Worker             // Implement faceforward as `N ^ ((0 <= dot(I, NRef)) & 0x80000000)`.
3538*c8dee2aaSAndroid Build Coastguard Worker             // In other words, flip the sign bit of N if `0 <= dot(I, NRef)`.
3539*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().matches(arg1.type()));
3540*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().matches(arg2.type()));
3541*c8dee2aaSAndroid Build Coastguard Worker             int slotCount = arg0.type().slotCount();
3542*c8dee2aaSAndroid Build Coastguard Worker 
3543*c8dee2aaSAndroid Build Coastguard Worker             // Stack: N, 0, I, Nref
3544*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(arg0)) {
3545*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3546*c8dee2aaSAndroid Build Coastguard Worker             }
3547*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.push_constant_f(0.0);
3548*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(arg1) || !this->pushExpression(arg2)) {
3549*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3550*c8dee2aaSAndroid Build Coastguard Worker             }
3551*c8dee2aaSAndroid Build Coastguard Worker             // Stack: N, 0, dot(I,NRef)
3552*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.dot_floats(slotCount);
3553*c8dee2aaSAndroid Build Coastguard Worker             // Stack: N, (0 <= dot(I,NRef))
3554*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.binary_op(BuilderOp::cmple_n_floats, 1);
3555*c8dee2aaSAndroid Build Coastguard Worker             // Stack: N, (0 <= dot(I,NRef)), 0x80000000
3556*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.push_constant_u(0x80000000);
3557*c8dee2aaSAndroid Build Coastguard Worker             // Stack: N, (0 <= dot(I,NRef)) & 0x80000000)
3558*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.binary_op(BuilderOp::bitwise_and_n_ints, 1);
3559*c8dee2aaSAndroid Build Coastguard Worker             // Stack: N, vec(0 <= dot(I,NRef)) & 0x80000000)
3560*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.push_duplicates(slotCount - 1);
3561*c8dee2aaSAndroid Build Coastguard Worker             // Stack: N ^ vec((0 <= dot(I,NRef)) & 0x80000000)
3562*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.binary_op(BuilderOp::bitwise_xor_n_ints, slotCount);
3563*c8dee2aaSAndroid Build Coastguard Worker             return true;
3564*c8dee2aaSAndroid Build Coastguard Worker         }
3565*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_mix_IntrinsicKind:
3566*c8dee2aaSAndroid Build Coastguard Worker             // Note: our SkRP mix op takes the interpolation point first, not the interpolants.
3567*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().matches(arg1.type()));
3568*c8dee2aaSAndroid Build Coastguard Worker             if (arg2.type().componentType().isFloat()) {
3569*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(arg0.type().componentType().matches(arg2.type().componentType()));
3570*c8dee2aaSAndroid Build Coastguard Worker                 if (!this->pushVectorizedExpression(arg2, arg0.type())) {
3571*c8dee2aaSAndroid Build Coastguard Worker                     return unsupported();
3572*c8dee2aaSAndroid Build Coastguard Worker                 }
3573*c8dee2aaSAndroid Build Coastguard Worker                 if (!this->pushExpression(arg0) || !this->pushExpression(arg1)) {
3574*c8dee2aaSAndroid Build Coastguard Worker                     return unsupported();
3575*c8dee2aaSAndroid Build Coastguard Worker                 }
3576*c8dee2aaSAndroid Build Coastguard Worker                 return this->ternaryOp(arg0.type(), kMixOps);
3577*c8dee2aaSAndroid Build Coastguard Worker             }
3578*c8dee2aaSAndroid Build Coastguard Worker             if (arg2.type().componentType().isBoolean()) {
3579*c8dee2aaSAndroid Build Coastguard Worker                 if (!this->pushExpression(arg2)) {
3580*c8dee2aaSAndroid Build Coastguard Worker                     return unsupported();
3581*c8dee2aaSAndroid Build Coastguard Worker                 }
3582*c8dee2aaSAndroid Build Coastguard Worker                 if (!this->pushExpression(arg0) || !this->pushExpression(arg1)) {
3583*c8dee2aaSAndroid Build Coastguard Worker                     return unsupported();
3584*c8dee2aaSAndroid Build Coastguard Worker                 }
3585*c8dee2aaSAndroid Build Coastguard Worker                 // The `mix_int` op isn't doing a lerp; it uses the third argument to select values
3586*c8dee2aaSAndroid Build Coastguard Worker                 // from the first and second arguments. It's safe for use with any type in arguments
3587*c8dee2aaSAndroid Build Coastguard Worker                 // 0 and 1.
3588*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.ternary_op(BuilderOp::mix_n_ints, arg0.type().slotCount());
3589*c8dee2aaSAndroid Build Coastguard Worker                 return true;
3590*c8dee2aaSAndroid Build Coastguard Worker             }
3591*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
3592*c8dee2aaSAndroid Build Coastguard Worker 
3593*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_refract_IntrinsicKind: {
3594*c8dee2aaSAndroid Build Coastguard Worker             // We always calculate refraction using vec4s, so we pad out unused N/I slots with zero.
3595*c8dee2aaSAndroid Build Coastguard Worker             int padding = 4 - arg0.type().slotCount();
3596*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(arg0)) {
3597*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3598*c8dee2aaSAndroid Build Coastguard Worker             }
3599*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.push_zeros(padding);
3600*c8dee2aaSAndroid Build Coastguard Worker 
3601*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(arg1)) {
3602*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3603*c8dee2aaSAndroid Build Coastguard Worker             }
3604*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.push_zeros(padding);
3605*c8dee2aaSAndroid Build Coastguard Worker 
3606*c8dee2aaSAndroid Build Coastguard Worker             // eta is always a scalar and doesn't need padding.
3607*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(arg2)) {
3608*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3609*c8dee2aaSAndroid Build Coastguard Worker             }
3610*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.refract_floats();
3611*c8dee2aaSAndroid Build Coastguard Worker 
3612*c8dee2aaSAndroid Build Coastguard Worker             // The result vector was returned as a vec4, so discard the extra columns.
3613*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.discard_stack(padding);
3614*c8dee2aaSAndroid Build Coastguard Worker             return true;
3615*c8dee2aaSAndroid Build Coastguard Worker         }
3616*c8dee2aaSAndroid Build Coastguard Worker         case IntrinsicKind::k_smoothstep_IntrinsicKind:
3617*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0.type().componentType().isFloat());
3618*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg1.type().matches(arg0.type()));
3619*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg2.type().componentType().isFloat());
3620*c8dee2aaSAndroid Build Coastguard Worker 
3621*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushVectorizedExpression(arg0, arg2.type()) ||
3622*c8dee2aaSAndroid Build Coastguard Worker                 !this->pushVectorizedExpression(arg1, arg2.type()) ||
3623*c8dee2aaSAndroid Build Coastguard Worker                 !this->pushExpression(arg2)) {
3624*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3625*c8dee2aaSAndroid Build Coastguard Worker             }
3626*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.ternary_op(BuilderOp::smoothstep_n_floats, arg2.type().slotCount());
3627*c8dee2aaSAndroid Build Coastguard Worker             return true;
3628*c8dee2aaSAndroid Build Coastguard Worker 
3629*c8dee2aaSAndroid Build Coastguard Worker         default:
3630*c8dee2aaSAndroid Build Coastguard Worker             break;
3631*c8dee2aaSAndroid Build Coastguard Worker     }
3632*c8dee2aaSAndroid Build Coastguard Worker     return unsupported();
3633*c8dee2aaSAndroid Build Coastguard Worker }
3634*c8dee2aaSAndroid Build Coastguard Worker 
pushLiteral(const Literal & l)3635*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushLiteral(const Literal& l) {
3636*c8dee2aaSAndroid Build Coastguard Worker     switch (l.type().numberKind()) {
3637*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kFloat:
3638*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.push_constant_f(l.floatValue());
3639*c8dee2aaSAndroid Build Coastguard Worker             return true;
3640*c8dee2aaSAndroid Build Coastguard Worker 
3641*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kSigned:
3642*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.push_constant_i(l.intValue());
3643*c8dee2aaSAndroid Build Coastguard Worker             return true;
3644*c8dee2aaSAndroid Build Coastguard Worker 
3645*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kUnsigned:
3646*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.push_constant_u(l.intValue());
3647*c8dee2aaSAndroid Build Coastguard Worker             return true;
3648*c8dee2aaSAndroid Build Coastguard Worker 
3649*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kBoolean:
3650*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.push_constant_i(l.boolValue() ? ~0 : 0);
3651*c8dee2aaSAndroid Build Coastguard Worker             return true;
3652*c8dee2aaSAndroid Build Coastguard Worker 
3653*c8dee2aaSAndroid Build Coastguard Worker         default:
3654*c8dee2aaSAndroid Build Coastguard Worker             SkUNREACHABLE;
3655*c8dee2aaSAndroid Build Coastguard Worker     }
3656*c8dee2aaSAndroid Build Coastguard Worker }
3657*c8dee2aaSAndroid Build Coastguard Worker 
pushPostfixExpression(const PostfixExpression & p,bool usesResult)3658*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushPostfixExpression(const PostfixExpression& p, bool usesResult) {
3659*c8dee2aaSAndroid Build Coastguard Worker     // If the result is ignored...
3660*c8dee2aaSAndroid Build Coastguard Worker     if (!usesResult) {
3661*c8dee2aaSAndroid Build Coastguard Worker         // ... just emit a prefix expression instead.
3662*c8dee2aaSAndroid Build Coastguard Worker         return this->pushPrefixExpression(p.getOperator(), *p.operand());
3663*c8dee2aaSAndroid Build Coastguard Worker     }
3664*c8dee2aaSAndroid Build Coastguard Worker     // Get the operand as an lvalue, and push it onto the stack as-is.
3665*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<LValue> lvalue = this->makeLValue(*p.operand());
3666*c8dee2aaSAndroid Build Coastguard Worker     if (!lvalue || !this->push(*lvalue)) {
3667*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
3668*c8dee2aaSAndroid Build Coastguard Worker     }
3669*c8dee2aaSAndroid Build Coastguard Worker 
3670*c8dee2aaSAndroid Build Coastguard Worker     // Push a scratch copy of the operand.
3671*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.push_clone(p.type().slotCount());
3672*c8dee2aaSAndroid Build Coastguard Worker 
3673*c8dee2aaSAndroid Build Coastguard Worker     // Increment or decrement the scratch copy by one.
3674*c8dee2aaSAndroid Build Coastguard Worker     Literal oneLiteral{Position{}, 1.0, &p.type().componentType()};
3675*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushVectorizedExpression(oneLiteral, p.type())) {
3676*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
3677*c8dee2aaSAndroid Build Coastguard Worker     }
3678*c8dee2aaSAndroid Build Coastguard Worker 
3679*c8dee2aaSAndroid Build Coastguard Worker     switch (p.getOperator().kind()) {
3680*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::PLUSPLUS:
3681*c8dee2aaSAndroid Build Coastguard Worker             if (!this->binaryOp(p.type(), kAddOps)) {
3682*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3683*c8dee2aaSAndroid Build Coastguard Worker             }
3684*c8dee2aaSAndroid Build Coastguard Worker             break;
3685*c8dee2aaSAndroid Build Coastguard Worker 
3686*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::MINUSMINUS:
3687*c8dee2aaSAndroid Build Coastguard Worker             if (!this->binaryOp(p.type(), kSubtractOps)) {
3688*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3689*c8dee2aaSAndroid Build Coastguard Worker             }
3690*c8dee2aaSAndroid Build Coastguard Worker             break;
3691*c8dee2aaSAndroid Build Coastguard Worker 
3692*c8dee2aaSAndroid Build Coastguard Worker         default:
3693*c8dee2aaSAndroid Build Coastguard Worker             SkUNREACHABLE;
3694*c8dee2aaSAndroid Build Coastguard Worker     }
3695*c8dee2aaSAndroid Build Coastguard Worker 
3696*c8dee2aaSAndroid Build Coastguard Worker     // Write the new value back to the operand.
3697*c8dee2aaSAndroid Build Coastguard Worker     if (!this->store(*lvalue)) {
3698*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
3699*c8dee2aaSAndroid Build Coastguard Worker     }
3700*c8dee2aaSAndroid Build Coastguard Worker 
3701*c8dee2aaSAndroid Build Coastguard Worker     // Discard the scratch copy, leaving only the original value as-is.
3702*c8dee2aaSAndroid Build Coastguard Worker     this->discardExpression(p.type().slotCount());
3703*c8dee2aaSAndroid Build Coastguard Worker     return true;
3704*c8dee2aaSAndroid Build Coastguard Worker }
3705*c8dee2aaSAndroid Build Coastguard Worker 
pushPrefixExpression(const PrefixExpression & p)3706*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushPrefixExpression(const PrefixExpression& p) {
3707*c8dee2aaSAndroid Build Coastguard Worker     return this->pushPrefixExpression(p.getOperator(), *p.operand());
3708*c8dee2aaSAndroid Build Coastguard Worker }
3709*c8dee2aaSAndroid Build Coastguard Worker 
pushPrefixExpression(Operator op,const Expression & expr)3710*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushPrefixExpression(Operator op, const Expression& expr) {
3711*c8dee2aaSAndroid Build Coastguard Worker     switch (op.kind()) {
3712*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::BITWISENOT:
3713*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::LOGICALNOT:
3714*c8dee2aaSAndroid Build Coastguard Worker             // Handle operators ! and ~.
3715*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(expr)) {
3716*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3717*c8dee2aaSAndroid Build Coastguard Worker             }
3718*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.push_constant_u(~0, expr.type().slotCount());
3719*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.binary_op(BuilderOp::bitwise_xor_n_ints, expr.type().slotCount());
3720*c8dee2aaSAndroid Build Coastguard Worker             return true;
3721*c8dee2aaSAndroid Build Coastguard Worker 
3722*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::MINUS: {
3723*c8dee2aaSAndroid Build Coastguard Worker             if (!this->pushExpression(expr)) {
3724*c8dee2aaSAndroid Build Coastguard Worker                 return unsupported();
3725*c8dee2aaSAndroid Build Coastguard Worker             }
3726*c8dee2aaSAndroid Build Coastguard Worker             if (expr.type().componentType().isFloat()) {
3727*c8dee2aaSAndroid Build Coastguard Worker                 // Handle float negation as an integer `x ^ 0x80000000`. This toggles the sign bit.
3728*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.push_constant_u(0x80000000, expr.type().slotCount());
3729*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.binary_op(BuilderOp::bitwise_xor_n_ints, expr.type().slotCount());
3730*c8dee2aaSAndroid Build Coastguard Worker             } else {
3731*c8dee2aaSAndroid Build Coastguard Worker                 // Handle integer negation as a componentwise `expr * -1`.
3732*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.push_constant_i(-1, expr.type().slotCount());
3733*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.binary_op(BuilderOp::mul_n_ints, expr.type().slotCount());
3734*c8dee2aaSAndroid Build Coastguard Worker             }
3735*c8dee2aaSAndroid Build Coastguard Worker             return true;
3736*c8dee2aaSAndroid Build Coastguard Worker         }
3737*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::PLUSPLUS: {
3738*c8dee2aaSAndroid Build Coastguard Worker             // Rewrite as `expr += 1`.
3739*c8dee2aaSAndroid Build Coastguard Worker             Literal oneLiteral{Position{}, 1.0, &expr.type().componentType()};
3740*c8dee2aaSAndroid Build Coastguard Worker             return this->pushBinaryExpression(expr, OperatorKind::PLUSEQ, oneLiteral);
3741*c8dee2aaSAndroid Build Coastguard Worker         }
3742*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::MINUSMINUS: {
3743*c8dee2aaSAndroid Build Coastguard Worker             // Rewrite as `expr += -1`.
3744*c8dee2aaSAndroid Build Coastguard Worker             Literal minusOneLiteral{expr.fPosition, -1.0, &expr.type().componentType()};
3745*c8dee2aaSAndroid Build Coastguard Worker             return this->pushBinaryExpression(expr, OperatorKind::PLUSEQ, minusOneLiteral);
3746*c8dee2aaSAndroid Build Coastguard Worker         }
3747*c8dee2aaSAndroid Build Coastguard Worker         default:
3748*c8dee2aaSAndroid Build Coastguard Worker             break;
3749*c8dee2aaSAndroid Build Coastguard Worker     }
3750*c8dee2aaSAndroid Build Coastguard Worker 
3751*c8dee2aaSAndroid Build Coastguard Worker     return unsupported();
3752*c8dee2aaSAndroid Build Coastguard Worker }
3753*c8dee2aaSAndroid Build Coastguard Worker 
pushSwizzle(const Swizzle & s)3754*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushSwizzle(const Swizzle& s) {
3755*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!s.components().empty() && s.components().size() <= 4);
3756*c8dee2aaSAndroid Build Coastguard Worker 
3757*c8dee2aaSAndroid Build Coastguard Worker     // If this is a simple subset of a variable's slots...
3758*c8dee2aaSAndroid Build Coastguard Worker     bool isSimpleSubset = is_sliceable_swizzle(s.components());
3759*c8dee2aaSAndroid Build Coastguard Worker     if (isSimpleSubset && s.base()->is<VariableReference>()) {
3760*c8dee2aaSAndroid Build Coastguard Worker         // ... we can just push part of the variable directly onto the stack, rather than pushing
3761*c8dee2aaSAndroid Build Coastguard Worker         // the whole expression and then immediately cutting it down. (Either way works, but this
3762*c8dee2aaSAndroid Build Coastguard Worker         // saves a step.)
3763*c8dee2aaSAndroid Build Coastguard Worker         return this->pushVariableReferencePartial(
3764*c8dee2aaSAndroid Build Coastguard Worker                 s.base()->as<VariableReference>(),
3765*c8dee2aaSAndroid Build Coastguard Worker                 SlotRange{/*index=*/s.components()[0], /*count=*/s.components().size()});
3766*c8dee2aaSAndroid Build Coastguard Worker     }
3767*c8dee2aaSAndroid Build Coastguard Worker     // Push the base expression.
3768*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushExpression(*s.base())) {
3769*c8dee2aaSAndroid Build Coastguard Worker         return false;
3770*c8dee2aaSAndroid Build Coastguard Worker     }
3771*c8dee2aaSAndroid Build Coastguard Worker     // An identity swizzle doesn't rearrange the data; it just (potentially) discards tail elements.
3772*c8dee2aaSAndroid Build Coastguard Worker     if (isSimpleSubset && s.components()[0] == 0) {
3773*c8dee2aaSAndroid Build Coastguard Worker         int discardedElements = s.base()->type().slotCount() - s.components().size();
3774*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(discardedElements >= 0);
3775*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.discard_stack(discardedElements);
3776*c8dee2aaSAndroid Build Coastguard Worker         return true;
3777*c8dee2aaSAndroid Build Coastguard Worker     }
3778*c8dee2aaSAndroid Build Coastguard Worker     // Perform the swizzle.
3779*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.swizzle(s.base()->type().slotCount(), s.components());
3780*c8dee2aaSAndroid Build Coastguard Worker     return true;
3781*c8dee2aaSAndroid Build Coastguard Worker }
3782*c8dee2aaSAndroid Build Coastguard Worker 
pushTernaryExpression(const TernaryExpression & t)3783*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushTernaryExpression(const TernaryExpression& t) {
3784*c8dee2aaSAndroid Build Coastguard Worker     return this->pushTernaryExpression(*t.test(), *t.ifTrue(), *t.ifFalse());
3785*c8dee2aaSAndroid Build Coastguard Worker }
3786*c8dee2aaSAndroid Build Coastguard Worker 
pushDynamicallyUniformTernaryExpression(const Expression & test,const Expression & ifTrue,const Expression & ifFalse)3787*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushDynamicallyUniformTernaryExpression(const Expression& test,
3788*c8dee2aaSAndroid Build Coastguard Worker                                                         const Expression& ifTrue,
3789*c8dee2aaSAndroid Build Coastguard Worker                                                         const Expression& ifFalse) {
3790*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(Analysis::IsDynamicallyUniformExpression(test));
3791*c8dee2aaSAndroid Build Coastguard Worker 
3792*c8dee2aaSAndroid Build Coastguard Worker     int falseLabelID = fBuilder.nextLabelID();
3793*c8dee2aaSAndroid Build Coastguard Worker     int exitLabelID = fBuilder.nextLabelID();
3794*c8dee2aaSAndroid Build Coastguard Worker 
3795*c8dee2aaSAndroid Build Coastguard Worker     // First, push the test-expression into a separate stack.
3796*c8dee2aaSAndroid Build Coastguard Worker     AutoStack testStack(this);
3797*c8dee2aaSAndroid Build Coastguard Worker     testStack.enter();
3798*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushExpression(test)) {
3799*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
3800*c8dee2aaSAndroid Build Coastguard Worker     }
3801*c8dee2aaSAndroid Build Coastguard Worker 
3802*c8dee2aaSAndroid Build Coastguard Worker     // Branch to the true- or false-expression based on the test-expression. We can skip the
3803*c8dee2aaSAndroid Build Coastguard Worker     // non-true path entirely since the test is known to be uniform.
3804*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.branch_if_no_active_lanes_on_stack_top_equal(~0, falseLabelID);
3805*c8dee2aaSAndroid Build Coastguard Worker     testStack.exit();
3806*c8dee2aaSAndroid Build Coastguard Worker 
3807*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushExpression(ifTrue)) {
3808*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
3809*c8dee2aaSAndroid Build Coastguard Worker     }
3810*c8dee2aaSAndroid Build Coastguard Worker 
3811*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.jump(exitLabelID);
3812*c8dee2aaSAndroid Build Coastguard Worker 
3813*c8dee2aaSAndroid Build Coastguard Worker     // The builder doesn't understand control flow, and assumes that every push moves the stack-top
3814*c8dee2aaSAndroid Build Coastguard Worker     // forwards. We need to manually balance out the `pushExpression` from the if-true path by
3815*c8dee2aaSAndroid Build Coastguard Worker     // moving the stack position backwards, so that the if-false path pushes its expression into the
3816*c8dee2aaSAndroid Build Coastguard Worker     // same as the if-true result.
3817*c8dee2aaSAndroid Build Coastguard Worker     this->discardExpression(/*slots=*/ifTrue.type().slotCount());
3818*c8dee2aaSAndroid Build Coastguard Worker 
3819*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.label(falseLabelID);
3820*c8dee2aaSAndroid Build Coastguard Worker 
3821*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushExpression(ifFalse)) {
3822*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
3823*c8dee2aaSAndroid Build Coastguard Worker     }
3824*c8dee2aaSAndroid Build Coastguard Worker 
3825*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.label(exitLabelID);
3826*c8dee2aaSAndroid Build Coastguard Worker 
3827*c8dee2aaSAndroid Build Coastguard Worker     // Jettison the text-expression from the separate stack.
3828*c8dee2aaSAndroid Build Coastguard Worker     testStack.enter();
3829*c8dee2aaSAndroid Build Coastguard Worker     this->discardExpression(/*slots=*/1);
3830*c8dee2aaSAndroid Build Coastguard Worker     testStack.exit();
3831*c8dee2aaSAndroid Build Coastguard Worker     return true;
3832*c8dee2aaSAndroid Build Coastguard Worker }
3833*c8dee2aaSAndroid Build Coastguard Worker 
pushTernaryExpression(const Expression & test,const Expression & ifTrue,const Expression & ifFalse)3834*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushTernaryExpression(const Expression& test,
3835*c8dee2aaSAndroid Build Coastguard Worker                                       const Expression& ifTrue,
3836*c8dee2aaSAndroid Build Coastguard Worker                                       const Expression& ifFalse) {
3837*c8dee2aaSAndroid Build Coastguard Worker     // If the test-expression is dynamically-uniform, we can skip over the non-true expressions
3838*c8dee2aaSAndroid Build Coastguard Worker     // entirely, and not need to involve the condition mask.
3839*c8dee2aaSAndroid Build Coastguard Worker     if (Analysis::IsDynamicallyUniformExpression(test)) {
3840*c8dee2aaSAndroid Build Coastguard Worker         return this->pushDynamicallyUniformTernaryExpression(test, ifTrue, ifFalse);
3841*c8dee2aaSAndroid Build Coastguard Worker     }
3842*c8dee2aaSAndroid Build Coastguard Worker 
3843*c8dee2aaSAndroid Build Coastguard Worker     // Analyze the ternary to see which corners we can safely cut.
3844*c8dee2aaSAndroid Build Coastguard Worker     bool ifFalseHasSideEffects = Analysis::HasSideEffects(ifFalse);
3845*c8dee2aaSAndroid Build Coastguard Worker     bool ifTrueHasSideEffects  = Analysis::HasSideEffects(ifTrue);
3846*c8dee2aaSAndroid Build Coastguard Worker     bool ifTrueIsTrivial       = Analysis::IsTrivialExpression(ifTrue);
3847*c8dee2aaSAndroid Build Coastguard Worker     int  cleanupLabelID        = fBuilder.nextLabelID();
3848*c8dee2aaSAndroid Build Coastguard Worker 
3849*c8dee2aaSAndroid Build Coastguard Worker     // If the true- and false-expressions both lack side effects, we evaluate both of them safely
3850*c8dee2aaSAndroid Build Coastguard Worker     // without masking off their effects. In that case, we can emit both sides and use boolean mix
3851*c8dee2aaSAndroid Build Coastguard Worker     // to select the correct result without using the condition mask at all.
3852*c8dee2aaSAndroid Build Coastguard Worker     if (!ifFalseHasSideEffects && !ifTrueHasSideEffects && ifTrueIsTrivial) {
3853*c8dee2aaSAndroid Build Coastguard Worker         // Push all of the arguments to mix.
3854*c8dee2aaSAndroid Build Coastguard Worker         if (!this->pushVectorizedExpression(test, ifTrue.type())) {
3855*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
3856*c8dee2aaSAndroid Build Coastguard Worker         }
3857*c8dee2aaSAndroid Build Coastguard Worker         if (!this->pushExpression(ifFalse)) {
3858*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
3859*c8dee2aaSAndroid Build Coastguard Worker         }
3860*c8dee2aaSAndroid Build Coastguard Worker         if (!this->pushExpression(ifTrue)) {
3861*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
3862*c8dee2aaSAndroid Build Coastguard Worker         }
3863*c8dee2aaSAndroid Build Coastguard Worker         // Use boolean mix to select the true- or false-expression via the test-expression.
3864*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.ternary_op(BuilderOp::mix_n_ints, ifTrue.type().slotCount());
3865*c8dee2aaSAndroid Build Coastguard Worker         return true;
3866*c8dee2aaSAndroid Build Coastguard Worker     }
3867*c8dee2aaSAndroid Build Coastguard Worker 
3868*c8dee2aaSAndroid Build Coastguard Worker     // First, push the current condition-mask and the test-expression into a separate stack.
3869*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.enableExecutionMaskWrites();
3870*c8dee2aaSAndroid Build Coastguard Worker     AutoStack testStack(this);
3871*c8dee2aaSAndroid Build Coastguard Worker     testStack.enter();
3872*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.push_condition_mask();
3873*c8dee2aaSAndroid Build Coastguard Worker     if (!this->pushExpression(test)) {
3874*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
3875*c8dee2aaSAndroid Build Coastguard Worker     }
3876*c8dee2aaSAndroid Build Coastguard Worker     testStack.exit();
3877*c8dee2aaSAndroid Build Coastguard Worker 
3878*c8dee2aaSAndroid Build Coastguard Worker     // We can take some shortcuts with condition-mask handling if the false-expression is entirely
3879*c8dee2aaSAndroid Build Coastguard Worker     // side-effect free. (We can evaluate it without masking off its effects.) We always handle the
3880*c8dee2aaSAndroid Build Coastguard Worker     // condition mask properly for the test-expression and true-expression properly.
3881*c8dee2aaSAndroid Build Coastguard Worker     if (!ifFalseHasSideEffects) {
3882*c8dee2aaSAndroid Build Coastguard Worker         // Push the false-expression onto the primary stack.
3883*c8dee2aaSAndroid Build Coastguard Worker         if (!this->pushExpression(ifFalse)) {
3884*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
3885*c8dee2aaSAndroid Build Coastguard Worker         }
3886*c8dee2aaSAndroid Build Coastguard Worker 
3887*c8dee2aaSAndroid Build Coastguard Worker         // Next, merge the condition mask (on the separate stack) with the test expression.
3888*c8dee2aaSAndroid Build Coastguard Worker         testStack.enter();
3889*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.merge_condition_mask();
3890*c8dee2aaSAndroid Build Coastguard Worker         testStack.exit();
3891*c8dee2aaSAndroid Build Coastguard Worker 
3892*c8dee2aaSAndroid Build Coastguard Worker         // If no lanes are active, we can skip the true-expression entirely. This isn't super likely
3893*c8dee2aaSAndroid Build Coastguard Worker         // to happen, so it's probably only a win for non-trivial true-expressions.
3894*c8dee2aaSAndroid Build Coastguard Worker         if (!ifTrueIsTrivial) {
3895*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.branch_if_no_lanes_active(cleanupLabelID);
3896*c8dee2aaSAndroid Build Coastguard Worker         }
3897*c8dee2aaSAndroid Build Coastguard Worker 
3898*c8dee2aaSAndroid Build Coastguard Worker         // Push the true-expression onto the primary stack, immediately after the false-expression.
3899*c8dee2aaSAndroid Build Coastguard Worker         if (!this->pushExpression(ifTrue)) {
3900*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
3901*c8dee2aaSAndroid Build Coastguard Worker         }
3902*c8dee2aaSAndroid Build Coastguard Worker 
3903*c8dee2aaSAndroid Build Coastguard Worker         // Use a select to conditionally mask-merge the true-expression and false-expression lanes.
3904*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.select(/*slots=*/ifTrue.type().slotCount());
3905*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.label(cleanupLabelID);
3906*c8dee2aaSAndroid Build Coastguard Worker     } else {
3907*c8dee2aaSAndroid Build Coastguard Worker         // Merge the condition mask (on the separate stack) with the test expression.
3908*c8dee2aaSAndroid Build Coastguard Worker         testStack.enter();
3909*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.merge_condition_mask();
3910*c8dee2aaSAndroid Build Coastguard Worker         testStack.exit();
3911*c8dee2aaSAndroid Build Coastguard Worker 
3912*c8dee2aaSAndroid Build Coastguard Worker         // Push the true-expression onto the primary stack.
3913*c8dee2aaSAndroid Build Coastguard Worker         if (!this->pushExpression(ifTrue)) {
3914*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
3915*c8dee2aaSAndroid Build Coastguard Worker         }
3916*c8dee2aaSAndroid Build Coastguard Worker 
3917*c8dee2aaSAndroid Build Coastguard Worker         // Switch back to the test-expression stack and apply the inverted test condition.
3918*c8dee2aaSAndroid Build Coastguard Worker         testStack.enter();
3919*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.merge_inv_condition_mask();
3920*c8dee2aaSAndroid Build Coastguard Worker         testStack.exit();
3921*c8dee2aaSAndroid Build Coastguard Worker 
3922*c8dee2aaSAndroid Build Coastguard Worker         // Push the false-expression onto the primary stack, immediately after the true-expression.
3923*c8dee2aaSAndroid Build Coastguard Worker         if (!this->pushExpression(ifFalse)) {
3924*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
3925*c8dee2aaSAndroid Build Coastguard Worker         }
3926*c8dee2aaSAndroid Build Coastguard Worker 
3927*c8dee2aaSAndroid Build Coastguard Worker         // Use a select to conditionally mask-merge the true-expression and false-expression lanes;
3928*c8dee2aaSAndroid Build Coastguard Worker         // the mask is already set up for this.
3929*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.select(/*slots=*/ifTrue.type().slotCount());
3930*c8dee2aaSAndroid Build Coastguard Worker     }
3931*c8dee2aaSAndroid Build Coastguard Worker 
3932*c8dee2aaSAndroid Build Coastguard Worker     // Restore the condition-mask to its original state and jettison the test-expression.
3933*c8dee2aaSAndroid Build Coastguard Worker     testStack.enter();
3934*c8dee2aaSAndroid Build Coastguard Worker     this->discardExpression(/*slots=*/1);
3935*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.pop_condition_mask();
3936*c8dee2aaSAndroid Build Coastguard Worker     testStack.exit();
3937*c8dee2aaSAndroid Build Coastguard Worker 
3938*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.disableExecutionMaskWrites();
3939*c8dee2aaSAndroid Build Coastguard Worker     return true;
3940*c8dee2aaSAndroid Build Coastguard Worker }
3941*c8dee2aaSAndroid Build Coastguard Worker 
pushVariableReference(const VariableReference & var)3942*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushVariableReference(const VariableReference& var) {
3943*c8dee2aaSAndroid Build Coastguard Worker     // If we are pushing a constant-value variable, push the value directly; literal values are more
3944*c8dee2aaSAndroid Build Coastguard Worker     // amenable to optimization.
3945*c8dee2aaSAndroid Build Coastguard Worker     if (var.type().isScalar() || var.type().isVector()) {
3946*c8dee2aaSAndroid Build Coastguard Worker         if (const Expression* expr = ConstantFolder::GetConstantValueOrNull(var)) {
3947*c8dee2aaSAndroid Build Coastguard Worker             return this->pushExpression(*expr);
3948*c8dee2aaSAndroid Build Coastguard Worker         }
3949*c8dee2aaSAndroid Build Coastguard Worker         if (fImmutableVariables.contains(var.variable())) {
3950*c8dee2aaSAndroid Build Coastguard Worker             return this->pushExpression(*var.variable()->initialValue());
3951*c8dee2aaSAndroid Build Coastguard Worker         }
3952*c8dee2aaSAndroid Build Coastguard Worker     }
3953*c8dee2aaSAndroid Build Coastguard Worker     return this->pushVariableReferencePartial(var, SlotRange{0, (int)var.type().slotCount()});
3954*c8dee2aaSAndroid Build Coastguard Worker }
3955*c8dee2aaSAndroid Build Coastguard Worker 
pushVariableReferencePartial(const VariableReference & v,SlotRange subset)3956*c8dee2aaSAndroid Build Coastguard Worker bool Generator::pushVariableReferencePartial(const VariableReference& v, SlotRange subset) {
3957*c8dee2aaSAndroid Build Coastguard Worker     const Variable& var = *v.variable();
3958*c8dee2aaSAndroid Build Coastguard Worker     SlotRange r;
3959*c8dee2aaSAndroid Build Coastguard Worker     if (IsUniform(var)) {
3960*c8dee2aaSAndroid Build Coastguard Worker         // Push a uniform.
3961*c8dee2aaSAndroid Build Coastguard Worker         r = this->getUniformSlots(var);
3962*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(r.count == (int)var.type().slotCount());
3963*c8dee2aaSAndroid Build Coastguard Worker         r.index += subset.index;
3964*c8dee2aaSAndroid Build Coastguard Worker         r.count = subset.count;
3965*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.push_uniform(r);
3966*c8dee2aaSAndroid Build Coastguard Worker     } else if (fImmutableVariables.contains(&var)) {
3967*c8dee2aaSAndroid Build Coastguard Worker         // If we only need a single slot, we can push a constant. This saves a lookup, and can
3968*c8dee2aaSAndroid Build Coastguard Worker         // occasionally permit the use of an immediate-mode op.
3969*c8dee2aaSAndroid Build Coastguard Worker         if (subset.count == 1) {
3970*c8dee2aaSAndroid Build Coastguard Worker             const Expression& expr = *v.variable()->initialValue();
3971*c8dee2aaSAndroid Build Coastguard Worker             std::optional<ImmutableBits> bits = this->getImmutableBitsForSlot(expr, subset.index);
3972*c8dee2aaSAndroid Build Coastguard Worker             if (bits.has_value()) {
3973*c8dee2aaSAndroid Build Coastguard Worker                 fBuilder.push_constant_i(*bits);
3974*c8dee2aaSAndroid Build Coastguard Worker                 return true;
3975*c8dee2aaSAndroid Build Coastguard Worker             }
3976*c8dee2aaSAndroid Build Coastguard Worker         }
3977*c8dee2aaSAndroid Build Coastguard Worker         // Push the immutable slot range.
3978*c8dee2aaSAndroid Build Coastguard Worker         r = this->getImmutableSlots(var);
3979*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(r.count == (int)var.type().slotCount());
3980*c8dee2aaSAndroid Build Coastguard Worker         r.index += subset.index;
3981*c8dee2aaSAndroid Build Coastguard Worker         r.count = subset.count;
3982*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.push_immutable(r);
3983*c8dee2aaSAndroid Build Coastguard Worker     } else {
3984*c8dee2aaSAndroid Build Coastguard Worker         // Push the variable.
3985*c8dee2aaSAndroid Build Coastguard Worker         r = this->getVariableSlots(var);
3986*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(r.count == (int)var.type().slotCount());
3987*c8dee2aaSAndroid Build Coastguard Worker         r.index += subset.index;
3988*c8dee2aaSAndroid Build Coastguard Worker         r.count = subset.count;
3989*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.push_slots(r);
3990*c8dee2aaSAndroid Build Coastguard Worker     }
3991*c8dee2aaSAndroid Build Coastguard Worker     return true;
3992*c8dee2aaSAndroid Build Coastguard Worker }
3993*c8dee2aaSAndroid Build Coastguard Worker 
writeProgram(const FunctionDefinition & function)3994*c8dee2aaSAndroid Build Coastguard Worker bool Generator::writeProgram(const FunctionDefinition& function) {
3995*c8dee2aaSAndroid Build Coastguard Worker     fCurrentFunction = &function;
3996*c8dee2aaSAndroid Build Coastguard Worker 
3997*c8dee2aaSAndroid Build Coastguard Worker     if (fDebugTrace) {
3998*c8dee2aaSAndroid Build Coastguard Worker         // Copy the program source into the debug info so that it will be written in the trace file.
3999*c8dee2aaSAndroid Build Coastguard Worker         fDebugTrace->setSource(*fProgram.fSource);
4000*c8dee2aaSAndroid Build Coastguard Worker 
4001*c8dee2aaSAndroid Build Coastguard Worker         if (fWriteTraceOps) {
4002*c8dee2aaSAndroid Build Coastguard Worker             // The Raster Pipeline blitter generates centered pixel coordinates. (0.5, 1.5, 2.5,
4003*c8dee2aaSAndroid Build Coastguard Worker             // etc.) Add 0.5 to the requested trace coordinate to match this, then compare against
4004*c8dee2aaSAndroid Build Coastguard Worker             // src.rg, which contains the shader's coordinates. We keep this result in a dedicated
4005*c8dee2aaSAndroid Build Coastguard Worker             // trace-mask stack.
4006*c8dee2aaSAndroid Build Coastguard Worker             fTraceMask.emplace(this);
4007*c8dee2aaSAndroid Build Coastguard Worker             fTraceMask->enter();
4008*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.push_device_xy01();
4009*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.discard_stack(2);
4010*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.push_constant_f(fDebugTrace->fTraceCoord.fX + 0.5f);
4011*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.push_constant_f(fDebugTrace->fTraceCoord.fY + 0.5f);
4012*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.binary_op(BuilderOp::cmpeq_n_floats, 2);
4013*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.binary_op(BuilderOp::bitwise_and_n_ints, 1);
4014*c8dee2aaSAndroid Build Coastguard Worker             fTraceMask->exit();
4015*c8dee2aaSAndroid Build Coastguard Worker 
4016*c8dee2aaSAndroid Build Coastguard Worker             // Assemble a position-to-line-number mapping for the debugger.
4017*c8dee2aaSAndroid Build Coastguard Worker             this->calculateLineOffsets();
4018*c8dee2aaSAndroid Build Coastguard Worker         }
4019*c8dee2aaSAndroid Build Coastguard Worker     }
4020*c8dee2aaSAndroid Build Coastguard Worker 
4021*c8dee2aaSAndroid Build Coastguard Worker     // Assign slots to the parameters of main; copy src and dst into those slots as appropriate.
4022*c8dee2aaSAndroid Build Coastguard Worker     const SkSL::Variable* mainCoordsParam = function.declaration().getMainCoordsParameter();
4023*c8dee2aaSAndroid Build Coastguard Worker     const SkSL::Variable* mainInputColorParam = function.declaration().getMainInputColorParameter();
4024*c8dee2aaSAndroid Build Coastguard Worker     const SkSL::Variable* mainDestColorParam = function.declaration().getMainDestColorParameter();
4025*c8dee2aaSAndroid Build Coastguard Worker 
4026*c8dee2aaSAndroid Build Coastguard Worker     for (const SkSL::Variable* param : function.declaration().parameters()) {
4027*c8dee2aaSAndroid Build Coastguard Worker         if (param == mainCoordsParam) {
4028*c8dee2aaSAndroid Build Coastguard Worker             // Coordinates are passed via RG.
4029*c8dee2aaSAndroid Build Coastguard Worker             SlotRange fragCoord = this->getVariableSlots(*param);
4030*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(fragCoord.count == 2);
4031*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.store_src_rg(fragCoord);
4032*c8dee2aaSAndroid Build Coastguard Worker         } else if (param == mainInputColorParam) {
4033*c8dee2aaSAndroid Build Coastguard Worker             // Input colors are passed via RGBA.
4034*c8dee2aaSAndroid Build Coastguard Worker             SlotRange srcColor = this->getVariableSlots(*param);
4035*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(srcColor.count == 4);
4036*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.store_src(srcColor);
4037*c8dee2aaSAndroid Build Coastguard Worker         } else if (param == mainDestColorParam) {
4038*c8dee2aaSAndroid Build Coastguard Worker             // Dest colors are passed via dRGBA.
4039*c8dee2aaSAndroid Build Coastguard Worker             SlotRange destColor = this->getVariableSlots(*param);
4040*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(destColor.count == 4);
4041*c8dee2aaSAndroid Build Coastguard Worker             fBuilder.store_dst(destColor);
4042*c8dee2aaSAndroid Build Coastguard Worker         } else {
4043*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAIL("Invalid parameter to main()");
4044*c8dee2aaSAndroid Build Coastguard Worker             return unsupported();
4045*c8dee2aaSAndroid Build Coastguard Worker         }
4046*c8dee2aaSAndroid Build Coastguard Worker     }
4047*c8dee2aaSAndroid Build Coastguard Worker 
4048*c8dee2aaSAndroid Build Coastguard Worker     // Initialize the program.
4049*c8dee2aaSAndroid Build Coastguard Worker     fBuilder.init_lane_masks();
4050*c8dee2aaSAndroid Build Coastguard Worker 
4051*c8dee2aaSAndroid Build Coastguard Worker     // Emit global variables.
4052*c8dee2aaSAndroid Build Coastguard Worker     if (!this->writeGlobals()) {
4053*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
4054*c8dee2aaSAndroid Build Coastguard Worker     }
4055*c8dee2aaSAndroid Build Coastguard Worker 
4056*c8dee2aaSAndroid Build Coastguard Worker     // Invoke main().
4057*c8dee2aaSAndroid Build Coastguard Worker     std::optional<SlotRange> mainResult = this->writeFunction(function, function, /*arguments=*/{});
4058*c8dee2aaSAndroid Build Coastguard Worker     if (!mainResult.has_value()) {
4059*c8dee2aaSAndroid Build Coastguard Worker         return unsupported();
4060*c8dee2aaSAndroid Build Coastguard Worker     }
4061*c8dee2aaSAndroid Build Coastguard Worker 
4062*c8dee2aaSAndroid Build Coastguard Worker     // Move the result of main() from slots into RGBA.
4063*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(mainResult->count == 4);
4064*c8dee2aaSAndroid Build Coastguard Worker     if (this->needsFunctionResultSlots(fCurrentFunction)) {
4065*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.load_src(*mainResult);
4066*c8dee2aaSAndroid Build Coastguard Worker     } else {
4067*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.pop_src_rgba();
4068*c8dee2aaSAndroid Build Coastguard Worker     }
4069*c8dee2aaSAndroid Build Coastguard Worker 
4070*c8dee2aaSAndroid Build Coastguard Worker     // Discard the trace mask.
4071*c8dee2aaSAndroid Build Coastguard Worker     if (fTraceMask.has_value()) {
4072*c8dee2aaSAndroid Build Coastguard Worker         fTraceMask->enter();
4073*c8dee2aaSAndroid Build Coastguard Worker         fBuilder.discard_stack(1);
4074*c8dee2aaSAndroid Build Coastguard Worker         fTraceMask->exit();
4075*c8dee2aaSAndroid Build Coastguard Worker     }
4076*c8dee2aaSAndroid Build Coastguard Worker 
4077*c8dee2aaSAndroid Build Coastguard Worker     return true;
4078*c8dee2aaSAndroid Build Coastguard Worker }
4079*c8dee2aaSAndroid Build Coastguard Worker 
finish()4080*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<RP::Program> Generator::finish() {
4081*c8dee2aaSAndroid Build Coastguard Worker     return fBuilder.finish(fProgramSlots.slotCount(),
4082*c8dee2aaSAndroid Build Coastguard Worker                            fUniformSlots.slotCount(),
4083*c8dee2aaSAndroid Build Coastguard Worker                            fImmutableSlots.slotCount(),
4084*c8dee2aaSAndroid Build Coastguard Worker                            fDebugTrace);
4085*c8dee2aaSAndroid Build Coastguard Worker }
4086*c8dee2aaSAndroid Build Coastguard Worker 
4087*c8dee2aaSAndroid Build Coastguard Worker }  // namespace RP
4088*c8dee2aaSAndroid Build Coastguard Worker 
MakeRasterPipelineProgram(const SkSL::Program & program,const FunctionDefinition & function,DebugTracePriv * debugTrace,bool writeTraceOps)4089*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<RP::Program> MakeRasterPipelineProgram(const SkSL::Program& program,
4090*c8dee2aaSAndroid Build Coastguard Worker                                                        const FunctionDefinition& function,
4091*c8dee2aaSAndroid Build Coastguard Worker                                                        DebugTracePriv* debugTrace,
4092*c8dee2aaSAndroid Build Coastguard Worker                                                        bool writeTraceOps) {
4093*c8dee2aaSAndroid Build Coastguard Worker     RP::Generator generator(program, debugTrace, writeTraceOps);
4094*c8dee2aaSAndroid Build Coastguard Worker     if (!generator.writeProgram(function)) {
4095*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
4096*c8dee2aaSAndroid Build Coastguard Worker     }
4097*c8dee2aaSAndroid Build Coastguard Worker     return generator.finish();
4098*c8dee2aaSAndroid Build Coastguard Worker }
4099*c8dee2aaSAndroid Build Coastguard Worker 
4100*c8dee2aaSAndroid Build Coastguard Worker }  // namespace SkSL
4101