• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkSpan.h"
9 #include "include/private/SkSLDefines.h"
10 #include "include/private/SkSLIRNode.h"
11 #include "include/private/SkSLLayout.h"
12 #include "include/private/SkSLModifiers.h"
13 #include "include/private/SkSLProgramElement.h"
14 #include "include/private/SkSLStatement.h"
15 #include "include/private/SkSLString.h"
16 #include "include/private/base/SkTArray.h"
17 #include "include/sksl/SkSLOperator.h"
18 #include "include/sksl/SkSLPosition.h"
19 #include "src/base/SkStringView.h"
20 #include "src/core/SkTHash.h"
21 #include "src/sksl/SkSLAnalysis.h"
22 #include "src/sksl/SkSLBuiltinTypes.h"
23 #include "src/sksl/SkSLCompiler.h"
24 #include "src/sksl/SkSLConstantFolder.h"
25 #include "src/sksl/SkSLIntrinsicList.h"
26 #include "src/sksl/codegen/SkSLRasterPipelineBuilder.h"
27 #include "src/sksl/codegen/SkSLRasterPipelineCodeGenerator.h"
28 #include "src/sksl/ir/SkSLBinaryExpression.h"
29 #include "src/sksl/ir/SkSLBlock.h"
30 #include "src/sksl/ir/SkSLBreakStatement.h"
31 #include "src/sksl/ir/SkSLChildCall.h"
32 #include "src/sksl/ir/SkSLConstructor.h"
33 #include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
34 #include "src/sksl/ir/SkSLConstructorMatrixResize.h"
35 #include "src/sksl/ir/SkSLConstructorSplat.h"
36 #include "src/sksl/ir/SkSLContinueStatement.h"
37 #include "src/sksl/ir/SkSLDoStatement.h"
38 #include "src/sksl/ir/SkSLExpression.h"
39 #include "src/sksl/ir/SkSLExpressionStatement.h"
40 #include "src/sksl/ir/SkSLFieldAccess.h"
41 #include "src/sksl/ir/SkSLForStatement.h"
42 #include "src/sksl/ir/SkSLFunctionCall.h"
43 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
44 #include "src/sksl/ir/SkSLFunctionDefinition.h"
45 #include "src/sksl/ir/SkSLIfStatement.h"
46 #include "src/sksl/ir/SkSLIndexExpression.h"
47 #include "src/sksl/ir/SkSLLiteral.h"
48 #include "src/sksl/ir/SkSLPostfixExpression.h"
49 #include "src/sksl/ir/SkSLPrefixExpression.h"
50 #include "src/sksl/ir/SkSLProgram.h"
51 #include "src/sksl/ir/SkSLReturnStatement.h"
52 #include "src/sksl/ir/SkSLSwitchCase.h"
53 #include "src/sksl/ir/SkSLSwitchStatement.h"
54 #include "src/sksl/ir/SkSLSwizzle.h"
55 #include "src/sksl/ir/SkSLTernaryExpression.h"
56 #include "src/sksl/ir/SkSLType.h"
57 #include "src/sksl/ir/SkSLVarDeclarations.h"
58 #include "src/sksl/ir/SkSLVariable.h"
59 #include "src/sksl/ir/SkSLVariableReference.h"
60 #include "src/sksl/tracing/SkRPDebugTrace.h"
61 #include "src/sksl/tracing/SkSLDebugInfo.h"
62 
63 #include <algorithm>
64 #include <cstddef>
65 #include <cstdint>
66 #include <float.h>
67 #include <optional>
68 #include <string>
69 #include <string_view>
70 #include <utility>
71 #include <vector>
72 
73 namespace SkSL {
74 namespace RP {
75 
unsupported()76 static bool unsupported() {
77     // If MakeRasterPipelineProgram returns false, set a breakpoint here for more information.
78     return false;
79 }
80 
81 class SlotManager {
82 public:
SlotManager(std::vector<SlotDebugInfo> * i)83     SlotManager(std::vector<SlotDebugInfo>* i) : fSlotDebugInfo(i) {}
84 
85     /** Used by `create` to add this variable to SlotDebugInfo inside SkRPDebugTrace. */
86     void addSlotDebugInfoForGroup(const std::string& varName,
87                                   const Type& type,
88                                   Position pos,
89                                   int* groupIndex,
90                                   bool isFunctionReturnValue);
91     void addSlotDebugInfo(const std::string& varName,
92                           const Type& type,
93                           Position pos,
94                           bool isFunctionReturnValue);
95 
96     /** Creates slots associated with an SkSL variable or return value. */
97     SlotRange createSlots(std::string name,
98                           const Type& type,
99                           Position pos,
100                           bool isFunctionReturnValue);
101 
102     /**
103      * Creates a single temporary slot for scratch storage. Temporary slots can be recycled, which
104      * frees them up for reuse. Temporary slots are not assigned a name and have an arbitrary type.
105      */
106     SlotRange createTemporarySlot(const Type& type);
107     void recycleTemporarySlot(SlotRange temporarySlot);
108 
109 
110     /** Looks up the slots associated with an SkSL variable; creates the slot if necessary. */
111     SlotRange getVariableSlots(const Variable& v);
112 
113     /**
114      * Looks up the slots associated with an SkSL function's return value; creates the range if
115      * necessary. Note that recursion is never supported, so we don't need to maintain return values
116      * in a stack; we can just statically allocate one slot per function call-site.
117      */
118     SlotRange getFunctionSlots(const IRNode& callSite, const FunctionDeclaration& f);
119 
120     /** Returns the total number of slots consumed. */
slotCount() const121     int slotCount() const { return fSlotCount; }
122 
123 private:
makeTempName()124     std::string makeTempName() { return SkSL::String::printf("[temporary %d]", fTemporaryCount++); }
125 
126     SkTHashMap<const IRNode*, SlotRange> fSlotMap;
127     SkTArray<Slot> fRecycledSlots;
128     int fSlotCount = 0;
129     int fTemporaryCount = 0;
130     std::vector<SlotDebugInfo>* fSlotDebugInfo;
131 };
132 
133 class AutoContinueMask {
134 public:
135     AutoContinueMask() = default;
136 
~AutoContinueMask()137     ~AutoContinueMask() {
138         if (fSlotRange) {
139             fSlotManager->recycleTemporarySlot(*fSlotRange);
140             *fSlotRange = fPreviousSlotRange;
141         }
142     }
143 
enable(SlotManager * mgr,const Type & type,SlotRange * range)144     void enable(SlotManager* mgr, const Type& type, SlotRange* range) {
145         fSlotManager = mgr;
146         fSlotRange = range;
147         fPreviousSlotRange = *fSlotRange;
148         *fSlotRange = fSlotManager->createTemporarySlot(type);
149     }
150 
enterLoopBody(Builder & builder)151     void enterLoopBody(Builder& builder) {
152         if (fSlotRange) {
153             builder.zero_slots_unmasked(*fSlotRange);
154         }
155     }
156 
exitLoopBody(Builder & builder)157     void exitLoopBody(Builder& builder) {
158         if (fSlotRange) {
159             builder.reenable_loop_mask(*fSlotRange);
160         }
161     }
162 
163 private:
164     SlotManager* fSlotManager = nullptr;
165     SlotRange* fSlotRange = nullptr;
166     SlotRange fPreviousSlotRange;
167 };
168 
169 class LValue;
170 
171 class Generator {
172 public:
Generator(const SkSL::Program & program,SkRPDebugTrace * debugTrace)173     Generator(const SkSL::Program& program, SkRPDebugTrace* debugTrace)
174             : fProgram(program)
175             , fDebugTrace(debugTrace)
176             , fProgramSlots(debugTrace ? &debugTrace->fSlotInfo : nullptr)
177             , fUniformSlots(debugTrace ? &debugTrace->fUniformInfo : nullptr) {}
178 
179     /** Converts the SkSL main() function into a set of Instructions. */
180     bool writeProgram(const FunctionDefinition& function);
181 
182     /** Returns the generated program. */
183     std::unique_ptr<RP::Program> finish();
184 
185     /**
186      * Converts an SkSL function into a set of Instructions. Returns nullopt if the function
187      * contained unsupported statements or expressions.
188      */
189     std::optional<SlotRange> writeFunction(const IRNode& callSite,
190                                            const FunctionDefinition& function);
191 
192     /**
193      * Returns the slot index of this function inside the FunctionDebugInfo array in SkRPDebugTrace.
194      * The FunctionDebugInfo slot will be created if it doesn't already exist.
195      */
196     int getFunctionDebugInfo(const FunctionDeclaration& decl);
197 
198     /** Looks up the slots associated with an SkSL variable; creates the slot if necessary. */
getVariableSlots(const Variable & v)199     SlotRange getVariableSlots(const Variable& v) {
200         SkASSERT(!IsUniform(v));
201         return fProgramSlots.getVariableSlots(v);
202     }
203 
204     /** Looks up the slots associated with an SkSL uniform; creates the slot if necessary. */
getUniformSlots(const Variable & v)205     SlotRange getUniformSlots(const Variable& v) {
206         SkASSERT(IsUniform(v));
207         return fUniformSlots.getVariableSlots(v);
208     }
209 
210     /**
211      * Looks up the slots associated with an SkSL function's return value; creates the range if
212      * necessary. Note that recursion is never supported, so we don't need to maintain return values
213      * in a stack; we can just statically allocate one slot per function call-site.
214      */
getFunctionSlots(const IRNode & callSite,const FunctionDeclaration & f)215     SlotRange getFunctionSlots(const IRNode& callSite, const FunctionDeclaration& f) {
216         return fProgramSlots.getFunctionSlots(callSite, f);
217     }
218 
219     /**
220      * Creates an additional stack for the program to push values onto. The stack will not become
221      * actively in-use until `setCurrentStack` is called.
222      */
223     int createStack();
224 
225     /** Frees a stack generated by `createStack`. The freed stack must be completely empty. */
226     void recycleStack(int stackID);
227 
228     /** Redirects builder ops to point to a different stack (created by `createStack`). */
229     void setCurrentStack(int stackID);
230 
231     /** Reports the currently active stack. */
currentStack()232     int currentStack() {
233         return fCurrentStack;
234     }
235 
236     /**
237      * Returns an LValue for the passed-in expression; if the expression isn't supported as an
238      * LValue, returns nullptr.
239      */
240     std::unique_ptr<LValue> makeLValue(const Expression& e, bool allowScratch = false);
241 
242     /** Copies the top-of-stack value into this lvalue, without discarding it from the stack. */
243     [[nodiscard]] bool store(LValue& lvalue);
244 
245     /** Pushes the lvalue onto the top-of-stack. */
246     [[nodiscard]] bool push(LValue& lvalue);
247 
248     /** The Builder stitches our instructions together into Raster Pipeline code. */
builder()249     Builder* builder() { return &fBuilder; }
250 
251     /** Appends a statement to the program. */
252     [[nodiscard]] bool writeStatement(const Statement& s);
253     [[nodiscard]] bool writeBlock(const Block& b);
254     [[nodiscard]] bool writeBreakStatement(const BreakStatement& b);
255     [[nodiscard]] bool writeContinueStatement(const ContinueStatement& b);
256     [[nodiscard]] bool writeDoStatement(const DoStatement& d);
257     [[nodiscard]] bool writeExpressionStatement(const ExpressionStatement& e);
258     [[nodiscard]] bool writeMasklessForStatement(const ForStatement& f);
259     [[nodiscard]] bool writeForStatement(const ForStatement& f);
260     [[nodiscard]] bool writeGlobals();
261     [[nodiscard]] bool writeIfStatement(const IfStatement& i);
262     [[nodiscard]] bool writeDynamicallyUniformIfStatement(const IfStatement& i);
263     [[nodiscard]] bool writeReturnStatement(const ReturnStatement& r);
264     [[nodiscard]] bool writeSwitchStatement(const SwitchStatement& s);
265     [[nodiscard]] bool writeVarDeclaration(const VarDeclaration& v);
266 
267     /** Pushes an expression to the value stack. */
268     [[nodiscard]] bool pushBinaryExpression(const BinaryExpression& e);
269     [[nodiscard]] bool pushBinaryExpression(const Expression& left,
270                                             Operator op,
271                                             const Expression& right);
272     [[nodiscard]] bool pushChildCall(const ChildCall& c);
273     [[nodiscard]] bool pushConstructorCast(const AnyConstructor& c);
274     [[nodiscard]] bool pushConstructorCompound(const AnyConstructor& c);
275     [[nodiscard]] bool pushConstructorDiagonalMatrix(const ConstructorDiagonalMatrix& c);
276     [[nodiscard]] bool pushConstructorMatrixResize(const ConstructorMatrixResize& c);
277     [[nodiscard]] bool pushConstructorSplat(const ConstructorSplat& c);
278     [[nodiscard]] bool pushExpression(const Expression& e, bool usesResult = true);
279     [[nodiscard]] bool pushFieldAccess(const FieldAccess& f);
280     [[nodiscard]] bool pushFunctionCall(const FunctionCall& c);
281     [[nodiscard]] bool pushIndexExpression(const IndexExpression& i);
282     [[nodiscard]] bool pushIntrinsic(const FunctionCall& c);
283     [[nodiscard]] bool pushIntrinsic(IntrinsicKind intrinsic, const Expression& arg0);
284     [[nodiscard]] bool pushIntrinsic(IntrinsicKind intrinsic,
285                                      const Expression& arg0,
286                                      const Expression& arg1);
287     [[nodiscard]] bool pushIntrinsic(IntrinsicKind intrinsic,
288                                      const Expression& arg0,
289                                      const Expression& arg1,
290                                      const Expression& arg2);
291     [[nodiscard]] bool pushLiteral(const Literal& l);
292     [[nodiscard]] bool pushPostfixExpression(const PostfixExpression& p, bool usesResult);
293     [[nodiscard]] bool pushPrefixExpression(const PrefixExpression& p);
294     [[nodiscard]] bool pushPrefixExpression(Operator op, const Expression& expr);
295     [[nodiscard]] bool pushSwizzle(const Swizzle& s);
296     [[nodiscard]] bool pushTernaryExpression(const TernaryExpression& t);
297     [[nodiscard]] bool pushTernaryExpression(const Expression& test,
298                                              const Expression& ifTrue,
299                                              const Expression& ifFalse);
300     [[nodiscard]] bool pushDynamicallyUniformTernaryExpression(const Expression& test,
301                                                                const Expression& ifTrue,
302                                                                const Expression& ifFalse);
303     [[nodiscard]] bool pushVariableReference(const VariableReference& v);
304 
305     /** Pops an expression from the value stack and copies it into slots. */
popToSlotRange(SlotRange r)306     void popToSlotRange(SlotRange r) { fBuilder.pop_slots(r); }
popToSlotRangeUnmasked(SlotRange r)307     void popToSlotRangeUnmasked(SlotRange r) { fBuilder.pop_slots_unmasked(r); }
308 
309     /** Pops an expression from the value stack and discards it. */
discardExpression(int slots)310     void discardExpression(int slots) { fBuilder.discard_stack(slots); }
311 
312     /** Zeroes out a range of slots. */
zeroSlotRangeUnmasked(SlotRange r)313     void zeroSlotRangeUnmasked(SlotRange r) { fBuilder.zero_slots_unmasked(r); }
314 
315     /** Expression utilities. */
316     struct TypedOps {
317         BuilderOp fFloatOp;
318         BuilderOp fSignedOp;
319         BuilderOp fUnsignedOp;
320         BuilderOp fBooleanOp;
321     };
322 
323     static BuilderOp GetTypedOp(const SkSL::Type& type, const TypedOps& ops);
324 
325     [[nodiscard]] bool unaryOp(const SkSL::Type& type, const TypedOps& ops);
326     [[nodiscard]] bool binaryOp(const SkSL::Type& type, const TypedOps& ops);
327     [[nodiscard]] bool ternaryOp(const SkSL::Type& type, const TypedOps& ops);
328     [[nodiscard]] bool pushIntrinsic(const TypedOps& ops, const Expression& arg0);
329     [[nodiscard]] bool pushIntrinsic(const TypedOps& ops,
330                                      const Expression& arg0,
331                                      const Expression& arg1);
332     [[nodiscard]] bool pushIntrinsic(BuilderOp builderOp, const Expression& arg0);
333     [[nodiscard]] bool pushIntrinsic(BuilderOp builderOp,
334                                      const Expression& arg0,
335                                      const Expression& arg1);
336     [[nodiscard]] bool pushVectorizedExpression(const Expression& expr, const Type& vectorType);
337     [[nodiscard]] bool pushVariableReferencePartial(const VariableReference& v, SlotRange subset);
338     [[nodiscard]] bool pushLValueOrExpression(LValue* lvalue, const Expression& expr);
339     [[nodiscard]] bool pushMatrixMultiply(LValue* lvalue,
340                                           const Expression& left,
341                                           const Expression& right,
342                                           int leftColumns, int leftRows,
343                                           int rightColumns, int rightRows);
344     [[nodiscard]] bool pushStructuredComparison(LValue* left,
345                                                 Operator op,
346                                                 LValue* right,
347                                                 const Type& type);
348 
349     void foldWithMultiOp(BuilderOp op, int elements);
350     void foldComparisonOp(Operator op, int elements);
351 
352     BuilderOp getTypedOp(const SkSL::Type& type, const TypedOps& ops) const;
353 
needsReturnMask()354     bool needsReturnMask() {
355         Analysis::ReturnComplexity* complexity = fReturnComplexityMap.find(fCurrentFunction);
356         if (!complexity) {
357             complexity = fReturnComplexityMap.set(fCurrentFunction,
358                                                   Analysis::GetReturnComplexity(*fCurrentFunction));
359         }
360         return *complexity >= Analysis::ReturnComplexity::kEarlyReturns;
361     }
362 
IsUniform(const Variable & var)363     static bool IsUniform(const Variable& var) {
364        return var.modifiers().fFlags & Modifiers::kUniform_Flag;
365     }
366 
IsOutParameter(const Variable & var)367     static bool IsOutParameter(const Variable& var) {
368         return (var.modifiers().fFlags & (Modifiers::kIn_Flag | Modifiers::kOut_Flag)) ==
369                Modifiers::kOut_Flag;
370     }
371 
IsInoutParameter(const Variable & var)372     static bool IsInoutParameter(const Variable& var) {
373         return (var.modifiers().fFlags & (Modifiers::kIn_Flag | Modifiers::kOut_Flag)) ==
374                (Modifiers::kIn_Flag | Modifiers::kOut_Flag);
375     }
376 
377 private:
378     const SkSL::Program& fProgram;
379     Builder fBuilder;
380     SkRPDebugTrace* fDebugTrace = nullptr;
381     SkTHashMap<const Variable*, int> fChildEffectMap;
382 
383     SlotManager fProgramSlots;
384     SlotManager fUniformSlots;
385 
386     const FunctionDefinition* fCurrentFunction = nullptr;
387     SlotRange fCurrentFunctionResult;
388     SlotRange fCurrentContinueMask;
389     int fCurrentStack = 0;
390     int fNextStackID = 0;
391     SkTArray<int> fRecycledStacks;
392 
393     SkTHashMap<const FunctionDefinition*, Analysis::ReturnComplexity> fReturnComplexityMap;
394 
395     static constexpr auto kAbsOps = TypedOps{BuilderOp::abs_float,
396                                              BuilderOp::abs_int,
397                                              BuilderOp::unsupported,
398                                              BuilderOp::unsupported};
399     static constexpr auto kAddOps = TypedOps{BuilderOp::add_n_floats,
400                                              BuilderOp::add_n_ints,
401                                              BuilderOp::add_n_ints,
402                                              BuilderOp::unsupported};
403     static constexpr auto kSubtractOps = TypedOps{BuilderOp::sub_n_floats,
404                                                   BuilderOp::sub_n_ints,
405                                                   BuilderOp::sub_n_ints,
406                                                   BuilderOp::unsupported};
407     static constexpr auto kMultiplyOps = TypedOps{BuilderOp::mul_n_floats,
408                                                   BuilderOp::mul_n_ints,
409                                                   BuilderOp::mul_n_ints,
410                                                   BuilderOp::unsupported};
411     static constexpr auto kDivideOps = TypedOps{BuilderOp::div_n_floats,
412                                                 BuilderOp::div_n_ints,
413                                                 BuilderOp::div_n_uints,
414                                                 BuilderOp::unsupported};
415     static constexpr auto kLessThanOps = TypedOps{BuilderOp::cmplt_n_floats,
416                                                   BuilderOp::cmplt_n_ints,
417                                                   BuilderOp::cmplt_n_uints,
418                                                   BuilderOp::unsupported};
419     static constexpr auto kLessThanEqualOps = TypedOps{BuilderOp::cmple_n_floats,
420                                                        BuilderOp::cmple_n_ints,
421                                                        BuilderOp::cmple_n_uints,
422                                                        BuilderOp::unsupported};
423     static constexpr auto kEqualOps = TypedOps{BuilderOp::cmpeq_n_floats,
424                                                BuilderOp::cmpeq_n_ints,
425                                                BuilderOp::cmpeq_n_ints,
426                                                BuilderOp::cmpeq_n_ints};
427     static constexpr auto kNotEqualOps = TypedOps{BuilderOp::cmpne_n_floats,
428                                                   BuilderOp::cmpne_n_ints,
429                                                   BuilderOp::cmpne_n_ints,
430                                                   BuilderOp::cmpne_n_ints};
431     static constexpr auto kMinOps = TypedOps{BuilderOp::min_n_floats,
432                                              BuilderOp::min_n_ints,
433                                              BuilderOp::min_n_uints,
434                                              BuilderOp::min_n_uints};
435     static constexpr auto kMaxOps = TypedOps{BuilderOp::max_n_floats,
436                                              BuilderOp::max_n_ints,
437                                              BuilderOp::max_n_uints,
438                                              BuilderOp::max_n_uints};
439     static constexpr auto kMixOps = TypedOps{BuilderOp::mix_n_floats,
440                                              BuilderOp::unsupported,
441                                              BuilderOp::unsupported,
442                                              BuilderOp::unsupported};
443 };
444 
445 class AutoStack {
446 public:
AutoStack(Generator * g)447     explicit AutoStack(Generator* g)
448             : fGenerator(g)
449             , fStackID(g->createStack()) {}
450 
~AutoStack()451     ~AutoStack() {
452         fGenerator->recycleStack(fStackID);
453     }
454 
enter()455     void enter() {
456         fParentStackID = fGenerator->currentStack();
457         fGenerator->setCurrentStack(fStackID);
458     }
459 
exit()460     void exit() {
461         SkASSERT(fGenerator->currentStack() == fStackID);
462         fGenerator->setCurrentStack(fParentStackID);
463     }
464 
pushClone(int slots,int offsetFromStackTop=0)465     void pushClone(int slots, int offsetFromStackTop = 0) {
466         fGenerator->builder()->push_clone_from_stack(slots, fStackID, offsetFromStackTop);
467     }
468 
469 private:
470     Generator* fGenerator;
471     int fStackID = 0;
472     int fParentStackID = 0;
473 };
474 
475 class LValue {
476 public:
477     virtual ~LValue() = default;
478 
479     /** Returns true if this lvalue is actually writable--temporaries and uniforms are not. */
480     virtual bool isWritable() const = 0;
481 
482     /**
483      * Returns the slot range of the lvalue, after it is winnowed down to the selected field/index.
484      * The range is calculated assuming every dynamic index will evaluate to zero.
485      */
486     virtual SlotRange fixedSlotRange(Generator* gen) = 0;
487 
488     /** Pushes values directly onto the stack. */
489     [[nodiscard]] virtual bool push(Generator* gen,
490                                     SlotRange fixedOffset,
491                                     SkSpan<const int8_t> swizzle) = 0;
492 
493     /** Stores topmost values from the stack directly into the lvalue. */
494     [[nodiscard]] virtual bool store(Generator* gen,
495                                      SlotRange fixedOffset,
496                                      SkSpan<const int8_t> swizzle) = 0;
497 };
498 
499 class ScratchLValue final : public LValue {
500 public:
ScratchLValue(const Expression & e)501     explicit ScratchLValue(const Expression& e)
502             : fExpression(&e)
503             , fNumSlots(e.type().slotCount()) {}
504 
~ScratchLValue()505     ~ScratchLValue() override {
506         if (fGenerator && fDedicatedStack.has_value()) {
507             // Jettison the scratch expression.
508             fDedicatedStack->enter();
509             fGenerator->discardExpression(fNumSlots);
510             fDedicatedStack->exit();
511         }
512     }
513 
isWritable() const514     bool isWritable() const override {
515         return false;
516     }
517 
fixedSlotRange(Generator * gen)518     SlotRange fixedSlotRange(Generator* gen) override {
519         return SlotRange{0, fNumSlots};
520     }
521 
push(Generator * gen,SlotRange fixedOffset,SkSpan<const int8_t> swizzle)522     [[nodiscard]] bool push(Generator* gen,
523                             SlotRange fixedOffset,
524                             SkSpan<const int8_t> swizzle) override {
525         if (!fDedicatedStack.has_value()) {
526             // Push the scratch expression onto a dedicated stack.
527             fGenerator = gen;
528             fDedicatedStack.emplace(fGenerator);
529             fDedicatedStack->enter();
530             if (!fGenerator->pushExpression(*fExpression)) {
531                 return unsupported();
532             }
533             fDedicatedStack->exit();
534         }
535 
536         fDedicatedStack->pushClone(fixedOffset.count,
537                                    fNumSlots - fixedOffset.count - fixedOffset.index);
538         if (!swizzle.empty()) {
539             gen->builder()->swizzle(fixedOffset.count, swizzle);
540         }
541         return true;
542     }
543 
store(Generator *,SlotRange,SkSpan<const int8_t>)544     [[nodiscard]] bool store(Generator*, SlotRange, SkSpan<const int8_t>) override {
545         SkDEBUGFAIL("scratch lvalues cannot be stored into");
546         return unsupported();
547     }
548 
549 private:
550     Generator* fGenerator = nullptr;
551     const Expression* fExpression = nullptr;
552     std::optional<AutoStack> fDedicatedStack;
553     int fNumSlots = 0;
554 };
555 
556 class VariableLValue final : public LValue {
557 public:
VariableLValue(const Variable * v)558     explicit VariableLValue(const Variable* v) : fVariable(v) {}
559 
isWritable() const560     bool isWritable() const override {
561         return !Generator::IsUniform(*fVariable);
562     }
563 
fixedSlotRange(Generator * gen)564     SlotRange fixedSlotRange(Generator* gen) override {
565         return Generator::IsUniform(*fVariable) ? gen->getUniformSlots(*fVariable)
566                                                 : gen->getVariableSlots(*fVariable);
567     }
568 
push(Generator * gen,SlotRange fixedOffset,SkSpan<const int8_t> swizzle)569     [[nodiscard]] bool push(Generator* gen,
570                             SlotRange fixedOffset,
571                             SkSpan<const int8_t> swizzle) override {
572         if (Generator::IsUniform(*fVariable)) {
573             gen->builder()->push_uniform(fixedOffset);
574         } else {
575             gen->builder()->push_slots(fixedOffset);
576         }
577         if (!swizzle.empty()) {
578             gen->builder()->swizzle(fixedOffset.count, swizzle);
579         }
580         return true;
581     }
582 
store(Generator * gen,SlotRange fixedOffset,SkSpan<const int8_t> swizzle)583     [[nodiscard]] bool store(Generator* gen,
584                              SlotRange fixedOffset,
585                              SkSpan<const int8_t> swizzle) override {
586         SkASSERT(!Generator::IsUniform(*fVariable));
587 
588         if (swizzle.empty()) {
589             gen->builder()->copy_stack_to_slots(fixedOffset, fixedOffset.count);
590         } else {
591             gen->builder()->swizzle_copy_stack_to_slots(fixedOffset, swizzle, swizzle.size());
592         }
593         return true;
594     }
595 
596 private:
597     const Variable* fVariable;
598 };
599 
600 class SwizzleLValue final : public LValue {
601 public:
SwizzleLValue(std::unique_ptr<LValue> p,const ComponentArray & c)602     explicit SwizzleLValue(std::unique_ptr<LValue> p, const ComponentArray& c)
603             : fParent(std::move(p))
604             , fComponents(c) {
605         SkASSERT(!fComponents.empty() && fComponents.size() <= 4);
606     }
607 
isWritable() const608     bool isWritable() const override {
609         return fParent->isWritable();
610     }
611 
fixedSlotRange(Generator * gen)612     SlotRange fixedSlotRange(Generator* gen) override {
613         return fParent->fixedSlotRange(gen);
614     }
615 
push(Generator * gen,SlotRange fixedOffset,SkSpan<const int8_t> swizzle)616     [[nodiscard]] bool push(Generator* gen,
617                             SlotRange fixedOffset,
618                             SkSpan<const int8_t> swizzle) override {
619         if (!swizzle.empty()) {
620             SkDEBUGFAIL("swizzle-of-a-swizzle should have been folded out in front end");
621             return unsupported();
622         }
623         return fParent->push(gen, fixedOffset, fComponents);
624     }
625 
store(Generator * gen,SlotRange fixedOffset,SkSpan<const int8_t> swizzle)626     [[nodiscard]] bool store(Generator* gen,
627                              SlotRange fixedOffset,
628                              SkSpan<const int8_t> swizzle) override {
629         if (!swizzle.empty()) {
630             SkDEBUGFAIL("swizzle-of-a-swizzle should have been folded out in front end");
631             return unsupported();
632         }
633         return fParent->store(gen, fixedOffset, fComponents);
634     }
635 
636 private:
637     std::unique_ptr<LValue> fParent;
638     const ComponentArray& fComponents;
639 };
640 
641 class UnownedLValueSlice : public LValue {
642 public:
UnownedLValueSlice(LValue * p,int initialSlot,int numSlots)643     explicit UnownedLValueSlice(LValue* p, int initialSlot, int numSlots)
644             : fParent(p)
645             , fInitialSlot(initialSlot)
646             , fNumSlots(numSlots) {
647         SkASSERT(fInitialSlot >= 0);
648         SkASSERT(fNumSlots > 0);
649     }
650 
isWritable() const651     bool isWritable() const override {
652         return fParent->isWritable();
653     }
654 
fixedSlotRange(Generator * gen)655     SlotRange fixedSlotRange(Generator* gen) override {
656         SlotRange range = fParent->fixedSlotRange(gen);
657         SlotRange adjusted = range;
658         adjusted.index += fInitialSlot;
659         adjusted.count = fNumSlots;
660         SkASSERT((adjusted.index + adjusted.count) <= (range.index + range.count));
661         return adjusted;
662     }
663 
push(Generator * gen,SlotRange fixedOffset,SkSpan<const int8_t> swizzle)664     [[nodiscard]] bool push(Generator* gen,
665                             SlotRange fixedOffset,
666                             SkSpan<const int8_t> swizzle) override {
667         return fParent->push(gen, fixedOffset, swizzle);
668     }
669 
store(Generator * gen,SlotRange fixedOffset,SkSpan<const int8_t> swizzle)670     [[nodiscard]] bool store(Generator* gen,
671                              SlotRange fixedOffset,
672                              SkSpan<const int8_t> swizzle) override {
673         return fParent->store(gen, fixedOffset, swizzle);
674     }
675 
676 protected:
677     LValue* fParent;
678 
679 private:
680     int fInitialSlot = 0;
681     int fNumSlots = 0;
682 };
683 
684 class LValueSlice final : public UnownedLValueSlice {
685 public:
LValueSlice(std::unique_ptr<LValue> p,int initialSlot,int numSlots)686     explicit LValueSlice(std::unique_ptr<LValue> p, int initialSlot, int numSlots)
687             : UnownedLValueSlice(p.release(), initialSlot, numSlots) {}
688 
~LValueSlice()689     ~LValueSlice() override {
690         delete fParent;
691     }
692 };
693 
addSlotDebugInfoForGroup(const std::string & varName,const Type & type,Position pos,int * groupIndex,bool isFunctionReturnValue)694 void SlotManager::addSlotDebugInfoForGroup(const std::string& varName,
695                                            const Type& type,
696                                            Position pos,
697                                            int* groupIndex,
698                                            bool isFunctionReturnValue) {
699     SkASSERT(fSlotDebugInfo);
700     switch (type.typeKind()) {
701         case Type::TypeKind::kArray: {
702             int nslots = type.columns();
703             const Type& elemType = type.componentType();
704             for (int slot = 0; slot < nslots; ++slot) {
705                 this->addSlotDebugInfoForGroup(varName + "[" + std::to_string(slot) + "]", elemType,
706                                                pos, groupIndex, isFunctionReturnValue);
707             }
708             break;
709         }
710         case Type::TypeKind::kStruct: {
711             for (const Type::Field& field : type.fields()) {
712                 this->addSlotDebugInfoForGroup(varName + "." + std::string(field.fName),
713                                                *field.fType, pos, groupIndex,
714                                                isFunctionReturnValue);
715             }
716             break;
717         }
718         default:
719             SkASSERTF(0, "unsupported slot type %d", (int)type.typeKind());
720             [[fallthrough]];
721 
722         case Type::TypeKind::kScalar:
723         case Type::TypeKind::kVector:
724         case Type::TypeKind::kMatrix: {
725             Type::NumberKind numberKind = type.componentType().numberKind();
726             int nslots = type.slotCount();
727 
728             for (int slot = 0; slot < nslots; ++slot) {
729                 SlotDebugInfo slotInfo;
730                 slotInfo.name = varName;
731                 slotInfo.columns = type.columns();
732                 slotInfo.rows = type.rows();
733                 slotInfo.componentIndex = slot;
734                 slotInfo.groupIndex = (*groupIndex)++;
735                 slotInfo.numberKind = numberKind;
736                 slotInfo.pos = pos;
737                 slotInfo.fnReturnValue = isFunctionReturnValue ? 1 : -1;
738                 fSlotDebugInfo->push_back(std::move(slotInfo));
739             }
740             break;
741         }
742     }
743 }
744 
addSlotDebugInfo(const std::string & varName,const Type & type,Position pos,bool isFunctionReturnValue)745 void SlotManager::addSlotDebugInfo(const std::string& varName,
746                                    const Type& type,
747                                    Position pos,
748                                    bool isFunctionReturnValue) {
749     int groupIndex = 0;
750     this->addSlotDebugInfoForGroup(varName, type, pos, &groupIndex, isFunctionReturnValue);
751     SkASSERT((size_t)groupIndex == type.slotCount());
752 }
753 
createTemporarySlot(const Type & type)754 SlotRange SlotManager::createTemporarySlot(const Type& type) {
755     SkASSERT(type.slotCount() == 1);
756 
757     // If we have an available slot to reclaim, take it now.
758     if (!fRecycledSlots.empty()) {
759         SlotRange result = {fRecycledSlots.back(), 1};
760         fRecycledSlots.pop_back();
761         return result;
762     }
763 
764     // Synthesize a new temporary slot.
765     if (fSlotDebugInfo) {
766         // Our debug slot-info table should have the same length as the actual slot table.
767         SkASSERT(fSlotDebugInfo->size() == (size_t)fSlotCount);
768 
769         // Add this temporary slot to the debug slot-info table. It's just scratch space which can
770         // be reused over the course of execution, so it doesn't get a name or type (uint will do).
771         this->addSlotDebugInfo(this->makeTempName(), type, Position{},
772                                /*isFunctionReturnValue=*/false);
773 
774         // Confirm that we added the expected number of slots.
775         SkASSERT(fSlotDebugInfo->size() == (size_t)(fSlotCount + 1));
776     }
777 
778     SlotRange result = {fSlotCount, 1};
779     ++fSlotCount;
780     return result;
781 }
782 
recycleTemporarySlot(SlotRange temporarySlot)783 void SlotManager::recycleTemporarySlot(SlotRange temporarySlot) {
784     SkASSERT(temporarySlot.count == 1);
785     fRecycledSlots.push_back(temporarySlot.index);
786 }
787 
createSlots(std::string name,const Type & type,Position pos,bool isFunctionReturnValue)788 SlotRange SlotManager::createSlots(std::string name,
789                                    const Type& type,
790                                    Position pos,
791                                    bool isFunctionReturnValue) {
792     size_t nslots = type.slotCount();
793     if (nslots == 0) {
794         return {};
795     }
796     if (fSlotDebugInfo) {
797         // Our debug slot-info table should have the same length as the actual slot table.
798         SkASSERT(fSlotDebugInfo->size() == (size_t)fSlotCount);
799 
800         // Append slot names and types to our debug slot-info table.
801         fSlotDebugInfo->reserve(fSlotCount + nslots);
802         this->addSlotDebugInfo(name, type, pos, isFunctionReturnValue);
803 
804         // Confirm that we added the expected number of slots.
805         SkASSERT(fSlotDebugInfo->size() == (size_t)(fSlotCount + nslots));
806     }
807 
808     SlotRange result = {fSlotCount, (int)nslots};
809     fSlotCount += nslots;
810     return result;
811 }
812 
getVariableSlots(const Variable & v)813 SlotRange SlotManager::getVariableSlots(const Variable& v) {
814     SlotRange* entry = fSlotMap.find(&v);
815     if (entry != nullptr) {
816         return *entry;
817     }
818     SlotRange range = this->createSlots(std::string(v.name()),
819                                         v.type(),
820                                         v.fPosition,
821                                         /*isFunctionReturnValue=*/false);
822     fSlotMap.set(&v, range);
823     return range;
824 }
825 
getFunctionSlots(const IRNode & callSite,const FunctionDeclaration & f)826 SlotRange SlotManager::getFunctionSlots(const IRNode& callSite, const FunctionDeclaration& f) {
827     SlotRange* entry = fSlotMap.find(&callSite);
828     if (entry != nullptr) {
829         return *entry;
830     }
831     SlotRange range = this->createSlots("[" + std::string(f.name()) + "].result",
832                                         f.returnType(),
833                                         f.fPosition,
834                                         /*isFunctionReturnValue=*/true);
835     fSlotMap.set(&callSite, range);
836     return range;
837 }
838 
is_sliceable_swizzle(SkSpan<const int8_t> components)839 static bool is_sliceable_swizzle(SkSpan<const int8_t> components) {
840     // Determine if the swizzle rearranges its elements, or if it's a simple subset of its elements.
841     // (A simple subset would be a sequential non-repeating range of components, like `.xyz` or
842     // `.yzw` or `.z`, but not `.xx` or `.xz`, which can be accessed as a slice of the variable.)
843     for (size_t index = 1; index < components.size(); ++index) {
844         if (components[index] != int8_t(components[0] + index)) {
845             return false;
846         }
847     }
848     return true;
849 }
850 
makeLValue(const Expression & e,bool allowScratch)851 std::unique_ptr<LValue> Generator::makeLValue(const Expression& e, bool allowScratch) {
852     if (e.is<VariableReference>()) {
853         return std::make_unique<VariableLValue>(e.as<VariableReference>().variable());
854     }
855     if (e.is<Swizzle>()) {
856         const Swizzle& swizzleExpr = e.as<Swizzle>();
857         if (std::unique_ptr<LValue> base = this->makeLValue(*swizzleExpr.base(),
858                                                             allowScratch)) {
859             const ComponentArray& components = swizzleExpr.components();
860             if (is_sliceable_swizzle(components)) {
861                 // If the swizzle is a contiguous subset, we can represent it with a fixed slice.
862                 return std::make_unique<LValueSlice>(std::move(base), components[0],
863                                                      components.size());
864             }
865             return std::make_unique<SwizzleLValue>(std::move(base), components);
866         }
867         return nullptr;
868     }
869     if (e.is<FieldAccess>()) {
870         const FieldAccess& fieldExpr = e.as<FieldAccess>();
871         if (std::unique_ptr<LValue> base = this->makeLValue(*fieldExpr.base(),
872                                                             allowScratch)) {
873             // Represent field access with a slice.
874             return std::make_unique<LValueSlice>(std::move(base), fieldExpr.initialSlot(),
875                                                  fieldExpr.type().slotCount());
876         }
877         return nullptr;
878     }
879     if (e.is<IndexExpression>()) {
880         const IndexExpression& indexExpr = e.as<IndexExpression>();
881         if (std::unique_ptr<LValue> base = this->makeLValue(*indexExpr.base(),
882                                                             allowScratch)) {
883             // If the index is a compile-time constant, we can represent it with a fixed slice.
884             SKSL_INT indexValue;
885             if (ConstantFolder::GetConstantInt(*indexExpr.index(), &indexValue)) {
886                 int numSlots = indexExpr.type().slotCount();
887                 return std::make_unique<LValueSlice>(std::move(base), numSlots * indexValue,
888                                                      numSlots);
889             }
890 
891             // TODO(skia:13676): support non-constant indices
892         }
893         return nullptr;
894     }
895     if (allowScratch) {
896         // This path allows us to perform field- and index-accesses on an expression as if it were
897         // an lvalue, but is a temporary and shouldn't be written back to.
898         return std::make_unique<ScratchLValue>(e);
899     }
900     return nullptr;
901 }
902 
store(LValue & lvalue)903 bool Generator::store(LValue& lvalue) {
904     SkASSERT(lvalue.isWritable());
905     return lvalue.store(this, lvalue.fixedSlotRange(this), /*swizzle=*/{});
906 }
907 
push(LValue & lvalue)908 bool Generator::push(LValue& lvalue) {
909     return lvalue.push(this, lvalue.fixedSlotRange(this), /*swizzle=*/{});
910 }
911 
getFunctionDebugInfo(const FunctionDeclaration & decl)912 int Generator::getFunctionDebugInfo(const FunctionDeclaration& decl) {
913     SkASSERT(fDebugTrace);
914 
915     std::string name = decl.description();
916 
917     // When generating the debug trace, we typically mark every function as `noinline`. This makes
918     // the trace more confusing, since this isn't in the source program, so remove it.
919     static constexpr std::string_view kNoInline = "noinline ";
920     if (skstd::starts_with(name, kNoInline)) {
921         name = name.substr(kNoInline.size());
922     }
923 
924     // Look for a matching FunctionDebugInfo slot.
925     for (size_t index = 0; index < fDebugTrace->fFuncInfo.size(); ++index) {
926         if (fDebugTrace->fFuncInfo[index].name == name) {
927             return index;
928         }
929     }
930 
931     // We've never called this function before; create a new slot to hold its information.
932     int slot = (int)fDebugTrace->fFuncInfo.size();
933     fDebugTrace->fFuncInfo.push_back(FunctionDebugInfo{std::move(name)});
934     return slot;
935 }
936 
createStack()937 int Generator::createStack() {
938     if (!fRecycledStacks.empty()) {
939         int stackID = fRecycledStacks.back();
940         fRecycledStacks.pop_back();
941         return stackID;
942     }
943     return ++fNextStackID;
944 }
945 
recycleStack(int stackID)946 void Generator::recycleStack(int stackID) {
947     fRecycledStacks.push_back(stackID);
948 }
949 
setCurrentStack(int stackID)950 void Generator::setCurrentStack(int stackID) {
951     if (fCurrentStack != stackID) {
952         fCurrentStack = stackID;
953         fBuilder.set_current_stack(stackID);
954     }
955 }
956 
writeFunction(const IRNode & callSite,const FunctionDefinition & function)957 std::optional<SlotRange> Generator::writeFunction(const IRNode& callSite,
958                                                   const FunctionDefinition& function) {
959     [[maybe_unused]] int funcIndex = -1;
960     if (fDebugTrace) {
961         funcIndex = this->getFunctionDebugInfo(function.declaration());
962         SkASSERT(funcIndex >= 0);
963         // TODO(debugger): add trace for function-enter
964     }
965 
966     SlotRange lastFunctionResult = fCurrentFunctionResult;
967     fCurrentFunctionResult = this->getFunctionSlots(callSite, function.declaration());
968 
969     if (!this->writeStatement(*function.body())) {
970         return std::nullopt;
971     }
972 
973     SlotRange functionResult = fCurrentFunctionResult;
974     fCurrentFunctionResult = lastFunctionResult;
975 
976     if (fDebugTrace) {
977         // TODO(debugger): add trace for function-exit
978     }
979 
980     return functionResult;
981 }
982 
writeGlobals()983 bool Generator::writeGlobals() {
984     for (const ProgramElement* e : fProgram.elements()) {
985         if (e->is<GlobalVarDeclaration>()) {
986             const GlobalVarDeclaration& gvd = e->as<GlobalVarDeclaration>();
987             const VarDeclaration& decl = gvd.varDeclaration();
988             const Variable* var = decl.var();
989 
990             if (var->type().isEffectChild()) {
991                 // Associate each child effect variable with its numeric index.
992                 SkASSERT(!fChildEffectMap.find(var));
993                 int childEffectIndex = fChildEffectMap.count();
994                 fChildEffectMap[var] = childEffectIndex;
995                 continue;
996             }
997 
998             // Opaque types include child processors and GL objects (samplers, textures, etc).
999             // Of those, only child processors are legal variables.
1000             SkASSERT(!var->type().isVoid());
1001             SkASSERT(!var->type().isOpaque());
1002 
1003             // Builtin variables are system-defined, with special semantics.
1004             if (int builtin = var->modifiers().fLayout.fBuiltin; builtin >= 0) {
1005                 if (builtin == SK_FRAGCOORD_BUILTIN) {
1006                     fBuilder.store_device_xy01(this->getVariableSlots(*var));
1007                     continue;
1008                 }
1009                 // The only builtin variable exposed to runtime effects is sk_FragCoord.
1010                 return unsupported();
1011             }
1012 
1013             if (IsUniform(*var)) {
1014                 // Create the uniform slot map in first-to-last order.
1015                 (void)this->getUniformSlots(*var);
1016                 continue;
1017             }
1018 
1019             // Other globals are treated as normal variable declarations.
1020             if (!this->writeVarDeclaration(decl)) {
1021                 return unsupported();
1022             }
1023         }
1024     }
1025 
1026     return true;
1027 }
1028 
writeStatement(const Statement & s)1029 bool Generator::writeStatement(const Statement& s) {
1030     switch (s.kind()) {
1031         case Statement::Kind::kBlock:
1032             return this->writeBlock(s.as<Block>());
1033 
1034         case Statement::Kind::kBreak:
1035             return this->writeBreakStatement(s.as<BreakStatement>());
1036 
1037         case Statement::Kind::kContinue:
1038             return this->writeContinueStatement(s.as<ContinueStatement>());
1039 
1040         case Statement::Kind::kDo:
1041             return this->writeDoStatement(s.as<DoStatement>());
1042 
1043         case Statement::Kind::kExpression:
1044             return this->writeExpressionStatement(s.as<ExpressionStatement>());
1045 
1046         case Statement::Kind::kFor:
1047             return this->writeForStatement(s.as<ForStatement>());
1048 
1049         case Statement::Kind::kIf:
1050             return this->writeIfStatement(s.as<IfStatement>());
1051 
1052         case Statement::Kind::kNop:
1053             return true;
1054 
1055         case Statement::Kind::kReturn:
1056             return this->writeReturnStatement(s.as<ReturnStatement>());
1057 
1058         case Statement::Kind::kSwitch:
1059             return this->writeSwitchStatement(s.as<SwitchStatement>());
1060 
1061         case Statement::Kind::kVarDeclaration:
1062             return this->writeVarDeclaration(s.as<VarDeclaration>());
1063 
1064         default:
1065             return unsupported();
1066     }
1067 }
1068 
writeBlock(const Block & b)1069 bool Generator::writeBlock(const Block& b) {
1070     for (const std::unique_ptr<Statement>& stmt : b.children()) {
1071         if (!this->writeStatement(*stmt)) {
1072             return unsupported();
1073         }
1074     }
1075     return true;
1076 }
1077 
writeBreakStatement(const BreakStatement &)1078 bool Generator::writeBreakStatement(const BreakStatement&) {
1079     fBuilder.mask_off_loop_mask();
1080     return true;
1081 }
1082 
writeContinueStatement(const ContinueStatement &)1083 bool Generator::writeContinueStatement(const ContinueStatement&) {
1084     // This could be written as one hand-tuned RasterPipeline op, but for now, we reuse existing ops
1085     // to assemble a continue op.
1086 
1087     // Set any currently-executing lanes in the continue-mask to true via push-pop.
1088     SkASSERT(fCurrentContinueMask.count == 1);
1089     fBuilder.push_literal_i(~0);
1090     this->popToSlotRange(fCurrentContinueMask);
1091 
1092     // Disable any currently-executing lanes from the loop mask.
1093     fBuilder.mask_off_loop_mask();
1094     return true;
1095 }
1096 
writeDoStatement(const DoStatement & d)1097 bool Generator::writeDoStatement(const DoStatement& d) {
1098     // Save off the original loop mask.
1099     fBuilder.enableExecutionMaskWrites();
1100     fBuilder.push_loop_mask();
1101 
1102     // If `continue` is used in the loop...
1103     Analysis::LoopControlFlowInfo loopInfo = Analysis::GetLoopControlFlowInfo(*d.statement());
1104     AutoContinueMask autoContinueMask;
1105     if (loopInfo.fHasContinue) {
1106         // ... create a temporary slot for continue-mask storage.
1107         autoContinueMask.enable(&fProgramSlots, *fProgram.fContext->fTypes.fUInt,
1108                                 &fCurrentContinueMask);
1109     }
1110 
1111     // Write the do-loop body.
1112     int labelID = fBuilder.nextLabelID();
1113     fBuilder.label(labelID);
1114 
1115     autoContinueMask.enterLoopBody(fBuilder);
1116 
1117     if (!this->writeStatement(*d.statement())) {
1118         return false;
1119     }
1120 
1121     autoContinueMask.exitLoopBody(fBuilder);
1122 
1123     // Emit the test-expression, in order to combine it with the loop mask.
1124     if (!this->pushExpression(*d.test())) {
1125         return false;
1126     }
1127 
1128     // Mask off any lanes in the loop mask where the test-expression is false; this breaks the loop.
1129     // We don't use the test expression for anything else, so jettison it.
1130     fBuilder.merge_loop_mask();
1131     this->discardExpression(/*slots=*/1);
1132 
1133     // If any lanes are still running, go back to the top and run the loop body again.
1134     fBuilder.branch_if_any_active_lanes(labelID);
1135 
1136     // Restore the loop mask.
1137     fBuilder.pop_loop_mask();
1138     fBuilder.disableExecutionMaskWrites();
1139 
1140     return true;
1141 }
1142 
writeMasklessForStatement(const ForStatement & f)1143 bool Generator::writeMasklessForStatement(const ForStatement& f) {
1144     SkASSERT(f.unrollInfo());
1145     SkASSERT(f.unrollInfo()->fCount > 0);
1146     SkASSERT(f.initializer());
1147     SkASSERT(f.test());
1148     SkASSERT(f.next());
1149 
1150     // If no lanes are active, skip over the loop entirely. This guards against looping forever;
1151     // with no lanes active, we wouldn't be able to write the loop variable back to its slot, so
1152     // we'd never make forward progress.
1153     int loopExitID = fBuilder.nextLabelID();
1154     int loopBodyID = fBuilder.nextLabelID();
1155     fBuilder.branch_if_no_active_lanes(loopExitID);
1156 
1157     // Run the loop initializer.
1158     if (!this->writeStatement(*f.initializer())) {
1159         return unsupported();
1160     }
1161 
1162     // Write the for-loop body. We know the for-loop has a standard ES2 unrollable structure, and
1163     // that it runs for at least one iteration, so we can plow straight ahead into the loop body
1164     // instead of running the loop-test first.
1165     fBuilder.label(loopBodyID);
1166 
1167     if (!this->writeStatement(*f.statement())) {
1168         return unsupported();
1169     }
1170 
1171     // If the loop only runs for a single iteration, we are already done. If not...
1172     if (f.unrollInfo()->fCount > 1) {
1173         // ... run the next-expression, and immediately discard its result.
1174         if (!this->pushExpression(*f.next(), /*usesResult=*/false)) {
1175             return unsupported();
1176         }
1177         this->discardExpression(f.next()->type().slotCount());
1178 
1179         // Run the test-expression, and repeat the loop until the test-expression evaluates false.
1180         if (!this->pushExpression(*f.test())) {
1181             return unsupported();
1182         }
1183         fBuilder.branch_if_no_active_lanes_on_stack_top_equal(0, loopBodyID);
1184 
1185         // Jettison the test-expression.
1186         this->discardExpression(/*slots=*/1);
1187     }
1188 
1189     fBuilder.label(loopExitID);
1190     return true;
1191 }
1192 
writeForStatement(const ForStatement & f)1193 bool Generator::writeForStatement(const ForStatement& f) {
1194     // If we've determined that the loop does not run, omit its code entirely.
1195     if (f.unrollInfo() && f.unrollInfo()->fCount == 0) {
1196         return true;
1197     }
1198 
1199     // If the loop doesn't escape early due to a `continue`, `break` or `return`, and the loop
1200     // conforms to ES2 structure, we know that we will run the full number of iterations across all
1201     // lanes and don't need to use a loop mask.
1202     Analysis::LoopControlFlowInfo loopInfo = Analysis::GetLoopControlFlowInfo(*f.statement());
1203     if (!loopInfo.fHasContinue && !loopInfo.fHasBreak && !loopInfo.fHasReturn && f.unrollInfo()) {
1204         return this->writeMasklessForStatement(f);
1205     }
1206 
1207     // Run the loop initializer.
1208     if (f.initializer() && !this->writeStatement(*f.initializer())) {
1209         return unsupported();
1210     }
1211 
1212     AutoContinueMask autoContinueMask;
1213     if (loopInfo.fHasContinue) {
1214         // Acquire a temporary slot for continue-mask storage.
1215         autoContinueMask.enable(&fProgramSlots, *fProgram.fContext->fTypes.fUInt,
1216                                 &fCurrentContinueMask);
1217     }
1218 
1219     // Save off the original loop mask.
1220     fBuilder.enableExecutionMaskWrites();
1221     fBuilder.push_loop_mask();
1222 
1223     int loopTestID = fBuilder.nextLabelID();
1224     int loopBodyID = fBuilder.nextLabelID();
1225 
1226     // Jump down to the loop test so we can fall out of the loop immediately if it's zero-iteration.
1227     fBuilder.jump(loopTestID);
1228 
1229     // Write the for-loop body.
1230     fBuilder.label(loopBodyID);
1231 
1232     autoContinueMask.enterLoopBody(fBuilder);
1233 
1234     if (!this->writeStatement(*f.statement())) {
1235         return unsupported();
1236     }
1237 
1238     autoContinueMask.exitLoopBody(fBuilder);
1239 
1240     // Run the next-expression. Immediately discard its result.
1241     if (f.next()) {
1242         if (!this->pushExpression(*f.next(), /*usesResult=*/false)) {
1243             return unsupported();
1244         }
1245         this->discardExpression(f.next()->type().slotCount());
1246     }
1247 
1248     fBuilder.label(loopTestID);
1249     if (f.test()) {
1250         // Emit the test-expression, in order to combine it with the loop mask.
1251         if (!this->pushExpression(*f.test())) {
1252             return unsupported();
1253         }
1254         // Mask off any lanes in the loop mask where the test-expression is false; this breaks the
1255         // loop. We don't use the test expression for anything else, so jettison it.
1256         fBuilder.merge_loop_mask();
1257         this->discardExpression(/*slots=*/1);
1258     }
1259 
1260     // If any lanes are still running, go back to the top and run the loop body again.
1261     fBuilder.branch_if_any_active_lanes(loopBodyID);
1262 
1263     // Restore the loop mask.
1264     fBuilder.pop_loop_mask();
1265     fBuilder.disableExecutionMaskWrites();
1266 
1267     return true;
1268 }
1269 
writeExpressionStatement(const ExpressionStatement & e)1270 bool Generator::writeExpressionStatement(const ExpressionStatement& e) {
1271     if (!this->pushExpression(*e.expression(), /*usesResult=*/false)) {
1272         return unsupported();
1273     }
1274     this->discardExpression(e.expression()->type().slotCount());
1275     return true;
1276 }
1277 
writeDynamicallyUniformIfStatement(const IfStatement & i)1278 bool Generator::writeDynamicallyUniformIfStatement(const IfStatement& i) {
1279     SkASSERT(Analysis::IsDynamicallyUniformExpression(*i.test()));
1280 
1281     int falseLabelID = fBuilder.nextLabelID();
1282     int exitLabelID = fBuilder.nextLabelID();
1283 
1284     if (!this->pushExpression(*i.test())) {
1285         return unsupported();
1286     }
1287 
1288     fBuilder.branch_if_no_active_lanes_on_stack_top_equal(~0, falseLabelID);
1289 
1290     if (!this->writeStatement(*i.ifTrue())) {
1291         return unsupported();
1292     }
1293 
1294     if (!i.ifFalse()) {
1295         // We don't have an if-false condition at all.
1296         fBuilder.label(falseLabelID);
1297     } else {
1298         // We do have an if-false condition. We've just completed the if-true block, so we need to
1299         // jump past the if-false block to avoid executing it.
1300         fBuilder.jump(exitLabelID);
1301 
1302         // The if-false block starts here.
1303         fBuilder.label(falseLabelID);
1304 
1305         if (!this->writeStatement(*i.ifFalse())) {
1306             return unsupported();
1307         }
1308 
1309         fBuilder.label(exitLabelID);
1310     }
1311 
1312     // Jettison the test-expression.
1313     this->discardExpression(/*slots=*/1);
1314     return true;
1315 }
1316 
writeIfStatement(const IfStatement & i)1317 bool Generator::writeIfStatement(const IfStatement& i) {
1318     // If the test condition is known to be uniform, we can skip over the untrue portion entirely.
1319     if (Analysis::IsDynamicallyUniformExpression(*i.test())) {
1320         return this->writeDynamicallyUniformIfStatement(i);
1321     }
1322 
1323     // Save the current condition-mask.
1324     fBuilder.enableExecutionMaskWrites();
1325     fBuilder.push_condition_mask();
1326 
1327     // Push the test condition mask.
1328     if (!this->pushExpression(*i.test())) {
1329         return unsupported();
1330     }
1331 
1332     // Merge the current condition-mask with the test condition, then run the if-true branch.
1333     fBuilder.merge_condition_mask();
1334     if (!this->writeStatement(*i.ifTrue())) {
1335         return unsupported();
1336     }
1337 
1338     if (i.ifFalse()) {
1339         // Negate the test-condition, then reapply it to the condition-mask.
1340         // Then, run the if-false branch.
1341         fBuilder.unary_op(BuilderOp::bitwise_not_int, /*slots=*/1);
1342         fBuilder.merge_condition_mask();
1343         if (!this->writeStatement(*i.ifFalse())) {
1344             return unsupported();
1345         }
1346     }
1347 
1348     // Jettison the test-expression, and restore the the condition-mask.
1349     this->discardExpression(/*slots=*/1);
1350     fBuilder.pop_condition_mask();
1351     fBuilder.disableExecutionMaskWrites();
1352 
1353     return true;
1354 }
1355 
writeReturnStatement(const ReturnStatement & r)1356 bool Generator::writeReturnStatement(const ReturnStatement& r) {
1357     if (r.expression()) {
1358         if (!this->pushExpression(*r.expression())) {
1359             return unsupported();
1360         }
1361         this->popToSlotRange(fCurrentFunctionResult);
1362     }
1363     if (fBuilder.executionMaskWritesAreEnabled() && this->needsReturnMask()) {
1364         fBuilder.mask_off_return_mask();
1365     }
1366     return true;
1367 }
1368 
writeSwitchStatement(const SwitchStatement & s)1369 bool Generator::writeSwitchStatement(const SwitchStatement& s) {
1370     const StatementArray& cases = s.cases();
1371     SkASSERT(std::all_of(cases.begin(), cases.end(), [](const std::unique_ptr<Statement>& stmt) {
1372         return stmt->is<SwitchCase>();
1373     }));
1374 
1375     // Save off the original loop mask.
1376     fBuilder.enableExecutionMaskWrites();
1377     fBuilder.push_loop_mask();
1378 
1379     // Push the switch-case value, and write a default-mask that enables every lane which already
1380     // has an active loop mask. As we match cases, the default mask will get pared down.
1381     if (!this->pushExpression(*s.value())) {
1382         return unsupported();
1383     }
1384     fBuilder.push_loop_mask();
1385 
1386     // Zero out the loop mask; each case op will re-enable it as we go.
1387     fBuilder.mask_off_loop_mask();
1388 
1389     // Write each switch-case.
1390     bool foundDefaultCase = false;
1391     for (const std::unique_ptr<Statement>& stmt : cases) {
1392         int skipLabelID = fBuilder.nextLabelID();
1393 
1394         const SwitchCase& sc = stmt->as<SwitchCase>();
1395         if (sc.isDefault()) {
1396             foundDefaultCase = true;
1397             if (stmt.get() != cases.back().get()) {
1398                 // We only support a default case when it is the very last case. If that changes,
1399                 // this logic will need to be updated.
1400                 return unsupported();
1401             }
1402             // Keep whatever lanes are executing now, and also enable any lanes in the default mask.
1403             fBuilder.pop_and_reenable_loop_mask();
1404             // Execute the switch-case block, if any lanes are alive to see it.
1405             fBuilder.branch_if_no_active_lanes(skipLabelID);
1406             if (!this->writeStatement(*sc.statement())) {
1407                 return unsupported();
1408             }
1409         } else {
1410             // The case-op will enable the loop mask if the switch-value matches, and mask off lanes
1411             // from the default-mask.
1412             fBuilder.case_op(sc.value());
1413             // Execute the switch-case block, if any lanes are alive to see it.
1414             fBuilder.branch_if_no_active_lanes(skipLabelID);
1415             if (!this->writeStatement(*sc.statement())) {
1416                 return unsupported();
1417             }
1418         }
1419         fBuilder.label(skipLabelID);
1420     }
1421 
1422     // Jettison the switch value, and the default case mask if it was never consumed above.
1423     this->discardExpression(/*slots=*/foundDefaultCase ? 1 : 2);
1424 
1425     // Restore the loop mask.
1426     fBuilder.pop_loop_mask();
1427     fBuilder.disableExecutionMaskWrites();
1428     return true;
1429 }
1430 
writeVarDeclaration(const VarDeclaration & v)1431 bool Generator::writeVarDeclaration(const VarDeclaration& v) {
1432     if (v.value()) {
1433         if (!this->pushExpression(*v.value())) {
1434             return unsupported();
1435         }
1436         this->popToSlotRangeUnmasked(this->getVariableSlots(*v.var()));
1437     } else {
1438         this->zeroSlotRangeUnmasked(this->getVariableSlots(*v.var()));
1439     }
1440     return true;
1441 }
1442 
pushExpression(const Expression & e,bool usesResult)1443 bool Generator::pushExpression(const Expression& e, bool usesResult) {
1444     switch (e.kind()) {
1445         case Expression::Kind::kBinary:
1446             return this->pushBinaryExpression(e.as<BinaryExpression>());
1447 
1448         case Expression::Kind::kChildCall:
1449             return this->pushChildCall(e.as<ChildCall>());
1450 
1451         case Expression::Kind::kConstructorCompound:
1452         case Expression::Kind::kConstructorStruct:
1453             return this->pushConstructorCompound(e.asAnyConstructor());
1454 
1455         case Expression::Kind::kConstructorCompoundCast:
1456         case Expression::Kind::kConstructorScalarCast:
1457             return this->pushConstructorCast(e.asAnyConstructor());
1458 
1459         case Expression::Kind::kConstructorDiagonalMatrix:
1460             return this->pushConstructorDiagonalMatrix(e.as<ConstructorDiagonalMatrix>());
1461 
1462         case Expression::Kind::kConstructorMatrixResize:
1463             return this->pushConstructorMatrixResize(e.as<ConstructorMatrixResize>());
1464 
1465         case Expression::Kind::kConstructorSplat:
1466             return this->pushConstructorSplat(e.as<ConstructorSplat>());
1467 
1468         case Expression::Kind::kFieldAccess:
1469             return this->pushFieldAccess(e.as<FieldAccess>());
1470 
1471         case Expression::Kind::kFunctionCall:
1472             return this->pushFunctionCall(e.as<FunctionCall>());
1473 
1474         case Expression::Kind::kIndex:
1475             return this->pushIndexExpression(e.as<IndexExpression>());
1476 
1477         case Expression::Kind::kLiteral:
1478             return this->pushLiteral(e.as<Literal>());
1479 
1480         case Expression::Kind::kPrefix:
1481             return this->pushPrefixExpression(e.as<PrefixExpression>());
1482 
1483         case Expression::Kind::kPostfix:
1484             return this->pushPostfixExpression(e.as<PostfixExpression>(), usesResult);
1485 
1486         case Expression::Kind::kSwizzle:
1487             return this->pushSwizzle(e.as<Swizzle>());
1488 
1489         case Expression::Kind::kTernary:
1490             return this->pushTernaryExpression(e.as<TernaryExpression>());
1491 
1492         case Expression::Kind::kVariableReference:
1493             return this->pushVariableReference(e.as<VariableReference>());
1494 
1495         default:
1496             return unsupported();
1497     }
1498 }
1499 
GetTypedOp(const SkSL::Type & type,const TypedOps & ops)1500 BuilderOp Generator::GetTypedOp(const SkSL::Type& type, const TypedOps& ops) {
1501     switch (type.componentType().numberKind()) {
1502         case Type::NumberKind::kFloat:    return ops.fFloatOp;
1503         case Type::NumberKind::kSigned:   return ops.fSignedOp;
1504         case Type::NumberKind::kUnsigned: return ops.fUnsignedOp;
1505         case Type::NumberKind::kBoolean:  return ops.fBooleanOp;
1506         default:                          return BuilderOp::unsupported;
1507     }
1508 }
1509 
unaryOp(const SkSL::Type & type,const TypedOps & ops)1510 bool Generator::unaryOp(const SkSL::Type& type, const TypedOps& ops) {
1511     BuilderOp op = GetTypedOp(type, ops);
1512     if (op == BuilderOp::unsupported) {
1513         return unsupported();
1514     }
1515     fBuilder.unary_op(op, type.slotCount());
1516     return true;
1517 }
1518 
binaryOp(const SkSL::Type & type,const TypedOps & ops)1519 bool Generator::binaryOp(const SkSL::Type& type, const TypedOps& ops) {
1520     BuilderOp op = GetTypedOp(type, ops);
1521     if (op == BuilderOp::unsupported) {
1522         return unsupported();
1523     }
1524     fBuilder.binary_op(op, type.slotCount());
1525     return true;
1526 }
1527 
ternaryOp(const SkSL::Type & type,const TypedOps & ops)1528 bool Generator::ternaryOp(const SkSL::Type& type, const TypedOps& ops) {
1529     BuilderOp op = GetTypedOp(type, ops);
1530     if (op == BuilderOp::unsupported) {
1531         return unsupported();
1532     }
1533     fBuilder.ternary_op(op, type.slotCount());
1534     return true;
1535 }
1536 
foldWithMultiOp(BuilderOp op,int elements)1537 void Generator::foldWithMultiOp(BuilderOp op, int elements) {
1538     // Fold the top N elements on the stack using an op that supports multiple slots, e.g.:
1539     // (A + B + C + D) -> add_2_floats $0..1 += $2..3
1540     //                    add_float    $0    += $1
1541     for (; elements >= 8; elements -= 4) {
1542         fBuilder.binary_op(op, /*slots=*/4);
1543     }
1544     for (; elements >= 6; elements -= 3) {
1545         fBuilder.binary_op(op, /*slots=*/3);
1546     }
1547     for (; elements >= 4; elements -= 2) {
1548         fBuilder.binary_op(op, /*slots=*/2);
1549     }
1550     for (; elements >= 2; elements -= 1) {
1551         fBuilder.binary_op(op, /*slots=*/1);
1552     }
1553 }
1554 
pushLValueOrExpression(LValue * lvalue,const Expression & expr)1555 bool Generator::pushLValueOrExpression(LValue* lvalue, const Expression& expr) {
1556     return lvalue ? this->push(*lvalue)
1557                   : this->pushExpression(expr);
1558 }
1559 
pushMatrixMultiply(LValue * lvalue,const Expression & left,const Expression & right,int leftColumns,int leftRows,int rightColumns,int rightRows)1560 bool Generator::pushMatrixMultiply(LValue* lvalue,
1561                                    const Expression& left,
1562                                    const Expression& right,
1563                                    int leftColumns,
1564                                    int leftRows,
1565                                    int rightColumns,
1566                                    int rightRows) {
1567     SkASSERT(left.type().isMatrix() || left.type().isVector());
1568     SkASSERT(right.type().isMatrix() || right.type().isVector());
1569 
1570     SkASSERT(leftColumns == rightRows);
1571     int outColumns   = rightColumns,
1572         outRows      = leftRows;
1573 
1574     // Push the left matrix onto the adjacent-neighbor stack. We transpose it so that we can copy
1575     // rows from it in a single op, instead of gathering one element at a time.
1576     AutoStack matrixStack(this);
1577     matrixStack.enter();
1578     if (!this->pushLValueOrExpression(lvalue, left)) {
1579         return unsupported();
1580     }
1581     fBuilder.transpose(leftColumns, leftRows);
1582 
1583     // Push the right matrix as well, then go back to the primary stack.
1584     if (!this->pushExpression(right)) {
1585         return unsupported();
1586     }
1587     matrixStack.exit();
1588 
1589     // Calculate the offsets of the left- and right-matrix, relative to the stack-top.
1590     int leftMtxBase  = left.type().slotCount() + right.type().slotCount() - leftColumns;
1591     int rightMtxBase = right.type().slotCount() - leftColumns;
1592 
1593     // Emit each matrix element.
1594     for (int c = 0; c < outColumns; ++c) {
1595         for (int r = 0; r < outRows; ++r) {
1596             // Dot a vector from left[*][r] with right[c][*].
1597             // (Because the left=matrix has been transposed, we actually pull left[r][*], which
1598             // allows us to clone a column at once instead of cloning each slot individually.)
1599             matrixStack.pushClone(leftColumns, leftMtxBase  - r * leftColumns);
1600             matrixStack.pushClone(leftColumns, rightMtxBase - c * leftColumns);
1601             fBuilder.dot_floats(leftColumns);
1602         }
1603     }
1604 
1605     // Dispose of the source matrices on the adjacent-neighbor stack.
1606     matrixStack.enter();
1607     this->discardExpression(left.type().slotCount());
1608     this->discardExpression(right.type().slotCount());
1609     matrixStack.exit();
1610 
1611     // If this multiply was actually an assignment (via *=), write the result back to the lvalue.
1612     return lvalue ? this->store(*lvalue)
1613                   : true;
1614 }
1615 
foldComparisonOp(Operator op,int elements)1616 void Generator::foldComparisonOp(Operator op, int elements) {
1617     switch (op.kind()) {
1618         case OperatorKind::EQEQ:
1619             // equal(x,y) returns a vector; use & to fold into a scalar.
1620             this->foldWithMultiOp(BuilderOp::bitwise_and_n_ints, elements);
1621             break;
1622 
1623         case OperatorKind::NEQ:
1624             // notEqual(x,y) returns a vector; use | to fold into a scalar.
1625             this->foldWithMultiOp(BuilderOp::bitwise_or_n_ints, elements);
1626             break;
1627 
1628         default:
1629             SkDEBUGFAIL("comparison only allows == and !=");
1630             break;
1631     }
1632 }
1633 
pushStructuredComparison(LValue * left,Operator op,LValue * right,const Type & type)1634 bool Generator::pushStructuredComparison(LValue* left,
1635                                          Operator op,
1636                                          LValue* right,
1637                                          const Type& type) {
1638     if (type.isStruct()) {
1639         // Compare every field in the struct.
1640         SkSpan<const Type::Field> fields = type.fields();
1641         int currentSlot = 0;
1642         for (size_t index = 0; index < fields.size(); ++index) {
1643             const Type& fieldType = *fields[index].fType;
1644             const int   fieldSlotCount = fieldType.slotCount();
1645             UnownedLValueSlice fieldLeft {left,  currentSlot, fieldSlotCount};
1646             UnownedLValueSlice fieldRight{right, currentSlot, fieldSlotCount};
1647             if (!this->pushStructuredComparison(&fieldLeft, op, &fieldRight, fieldType)) {
1648                 return unsupported();
1649             }
1650             currentSlot += fieldSlotCount;
1651         }
1652 
1653         this->foldComparisonOp(op, fields.size());
1654         return true;
1655     }
1656 
1657     if (type.isArray()) {
1658         const Type& indexedType = type.componentType();
1659         if (indexedType.numberKind() == Type::NumberKind::kNonnumeric) {
1660             // Compare every element in the array.
1661             const int indexedSlotCount = indexedType.slotCount();
1662             int       currentSlot = 0;
1663             for (int index = 0; index < type.columns(); ++index) {
1664                 UnownedLValueSlice indexedLeft {left,  currentSlot, indexedSlotCount};
1665                 UnownedLValueSlice indexedRight{right, currentSlot, indexedSlotCount};
1666                 if (!this->pushStructuredComparison(&indexedLeft, op, &indexedRight, indexedType)) {
1667                     return unsupported();
1668                 }
1669                 currentSlot += indexedSlotCount;
1670             }
1671 
1672             this->foldComparisonOp(op, type.columns());
1673             return true;
1674         }
1675     }
1676 
1677     // We've winnowed down to a single element, or an array of homogeneous numeric elements.
1678     // Push the elements onto the stack, then compare them.
1679     if (!this->push(*left) || !this->push(*right)) {
1680         return unsupported();
1681     }
1682     switch (op.kind()) {
1683         case OperatorKind::EQEQ:
1684             if (!this->binaryOp(type, kEqualOps)) {
1685                 return unsupported();
1686             }
1687             break;
1688 
1689         case OperatorKind::NEQ:
1690             if (!this->binaryOp(type, kNotEqualOps)) {
1691                 return unsupported();
1692             }
1693             break;
1694 
1695         default:
1696             SkDEBUGFAIL("comparison only allows == and !=");
1697             break;
1698     }
1699 
1700     this->foldComparisonOp(op, type.slotCount());
1701     return true;
1702 }
1703 
pushBinaryExpression(const BinaryExpression & e)1704 bool Generator::pushBinaryExpression(const BinaryExpression& e) {
1705     return this->pushBinaryExpression(*e.left(), e.getOperator(), *e.right());
1706 }
1707 
pushBinaryExpression(const Expression & left,Operator op,const Expression & right)1708 bool Generator::pushBinaryExpression(const Expression& left, Operator op, const Expression& right) {
1709     switch (op.kind()) {
1710         // Rewrite greater-than ops as their less-than equivalents.
1711         case OperatorKind::GT:
1712             return this->pushBinaryExpression(right, OperatorKind::LT, left);
1713 
1714         case OperatorKind::GTEQ:
1715             return this->pushBinaryExpression(right, OperatorKind::LTEQ, left);
1716 
1717         // Handle struct and array comparisons.
1718         case OperatorKind::EQEQ:
1719         case OperatorKind::NEQ:
1720             if (left.type().isStruct() || left.type().isArray()) {
1721                 SkASSERT(left.type().matches(right.type()));
1722                 std::unique_ptr<LValue> lvLeft = this->makeLValue(left, /*allowScratch=*/true);
1723                 std::unique_ptr<LValue> lvRight = this->makeLValue(right, /*allowScratch=*/true);
1724                 return this->pushStructuredComparison(lvLeft.get(), op, lvRight.get(), left.type());
1725             }
1726             break;
1727 
1728         // Emit comma expressions.
1729         case OperatorKind::COMMA:
1730             if (Analysis::HasSideEffects(left)) {
1731                 if (!this->pushExpression(left, /*usesResult=*/false)) {
1732                     return unsupported();
1733                 }
1734                 this->discardExpression(left.type().slotCount());
1735             }
1736             return this->pushExpression(right);
1737 
1738         default:
1739             break;
1740     }
1741 
1742     // Handle binary expressions with mismatched types.
1743     bool vectorizeLeft = false, vectorizeRight = false;
1744     if (!left.type().matches(right.type())) {
1745         if (left.type().componentType().numberKind() != right.type().componentType().numberKind()) {
1746             return unsupported();
1747         }
1748         if (left.type().isScalar() && (right.type().isVector() || right.type().isMatrix())) {
1749             vectorizeLeft = true;
1750         } else if ((left.type().isVector() || left.type().isMatrix()) && right.type().isScalar()) {
1751             vectorizeRight = true;
1752         }
1753     }
1754 
1755     const Type& type = vectorizeLeft ? right.type() : left.type();
1756 
1757     // If this is an assignment...
1758     std::unique_ptr<LValue> lvalue;
1759     if (op.isAssignment()) {
1760         // ... turn the left side into an lvalue.
1761         lvalue = this->makeLValue(left);
1762         if (!lvalue) {
1763             return unsupported();
1764         }
1765 
1766         // Handle simple assignment (`var = expr`).
1767         if (op.kind() == OperatorKind::EQ) {
1768             return this->pushExpression(right) &&
1769                    this->store(*lvalue);
1770         }
1771 
1772         // Strip off the assignment from the op (turning += into +).
1773         op = op.removeAssignment();
1774     }
1775 
1776     // Handle matrix multiplication (MxM/MxV/VxM).
1777     if (op.kind() == OperatorKind::STAR) {
1778         // Matrix * matrix:
1779         if (left.type().isMatrix() && right.type().isMatrix()) {
1780             return this->pushMatrixMultiply(lvalue.get(), left, right,
1781                                             left.type().columns(), left.type().rows(),
1782                                             right.type().columns(), right.type().rows());
1783         }
1784 
1785         // Vector * matrix:
1786         if (left.type().isVector() && right.type().isMatrix()) {
1787             return this->pushMatrixMultiply(lvalue.get(), left, right,
1788                                             left.type().columns(), 1,
1789                                             right.type().columns(), right.type().rows());
1790         }
1791 
1792         // Matrix * vector:
1793         if (left.type().isMatrix() && right.type().isVector()) {
1794             return this->pushMatrixMultiply(lvalue.get(), left, right,
1795                                             left.type().columns(), left.type().rows(),
1796                                             1, right.type().columns());
1797         }
1798     }
1799 
1800     if (!vectorizeLeft && !vectorizeRight && !type.matches(right.type())) {
1801         // We have mismatched types but don't know how to handle them.
1802         return unsupported();
1803     }
1804 
1805     // Handle binary ops which require short-circuiting.
1806     switch (op.kind()) {
1807         case OperatorKind::LOGICALAND:
1808             if (Analysis::HasSideEffects(right)) {
1809                 // If the RHS has side effects, we rewrite `a && b` as `a ? b : false`. This
1810                 // generates pretty solid code and gives us the required short-circuit behavior.
1811                 SkASSERT(!op.isAssignment());
1812                 SkASSERT(type.componentType().isBoolean());
1813                 SkASSERT(type.slotCount() == 1);  // operator&& only works with scalar types
1814                 Literal falseLiteral{Position{}, 0.0, &right.type()};
1815                 return this->pushTernaryExpression(left, right, falseLiteral);
1816             }
1817             break;
1818 
1819         case OperatorKind::LOGICALOR:
1820             if (Analysis::HasSideEffects(right)) {
1821                 // If the RHS has side effects, we rewrite `a || b` as `a ? true : b`.
1822                 SkASSERT(!op.isAssignment());
1823                 SkASSERT(type.componentType().isBoolean());
1824                 SkASSERT(type.slotCount() == 1);  // operator|| only works with scalar types
1825                 Literal trueLiteral{Position{}, 1.0, &right.type()};
1826                 return this->pushTernaryExpression(left, trueLiteral, right);
1827             }
1828             break;
1829 
1830         default:
1831             break;
1832     }
1833 
1834     // Push the left- and right-expressions onto the stack.
1835     if (!this->pushLValueOrExpression(lvalue.get(), left)) {
1836         return unsupported();
1837     }
1838     if (vectorizeLeft) {
1839         fBuilder.push_duplicates(right.type().slotCount() - 1);
1840     }
1841     if (!this->pushExpression(right)) {
1842         return unsupported();
1843     }
1844     if (vectorizeRight) {
1845         fBuilder.push_duplicates(left.type().slotCount() - 1);
1846     }
1847 
1848     switch (op.kind()) {
1849         case OperatorKind::PLUS:
1850             if (!this->binaryOp(type, kAddOps)) {
1851                 return unsupported();
1852             }
1853             break;
1854 
1855         case OperatorKind::MINUS:
1856             if (!this->binaryOp(type, kSubtractOps)) {
1857                 return unsupported();
1858             }
1859             break;
1860 
1861         case OperatorKind::STAR:
1862             if (!this->binaryOp(type, kMultiplyOps)) {
1863                 return unsupported();
1864             }
1865             break;
1866 
1867         case OperatorKind::SLASH:
1868             if (!this->binaryOp(type, kDivideOps)) {
1869                 return unsupported();
1870             }
1871             break;
1872 
1873         case OperatorKind::LT:
1874         case OperatorKind::GT:
1875             if (!this->binaryOp(type, kLessThanOps)) {
1876                 return unsupported();
1877             }
1878             SkASSERT(type.slotCount() == 1);  // operator< only works with scalar types
1879             break;
1880 
1881         case OperatorKind::LTEQ:
1882         case OperatorKind::GTEQ:
1883             if (!this->binaryOp(type, kLessThanEqualOps)) {
1884                 return unsupported();
1885             }
1886             SkASSERT(type.slotCount() == 1);  // operator<= only works with scalar types
1887             break;
1888 
1889         case OperatorKind::EQEQ:
1890             if (!this->binaryOp(type, kEqualOps)) {
1891                 return unsupported();
1892             }
1893             this->foldComparisonOp(op, type.slotCount());
1894             break;
1895 
1896         case OperatorKind::NEQ:
1897             if (!this->binaryOp(type, kNotEqualOps)) {
1898                 return unsupported();
1899             }
1900             this->foldComparisonOp(op, type.slotCount());
1901             break;
1902 
1903         case OperatorKind::LOGICALAND:
1904         case OperatorKind::BITWISEAND:
1905             // For logical-and, we verified above that the RHS does not have side effects, so we
1906             // don't need to worry about short-circuiting side effects.
1907             fBuilder.binary_op(BuilderOp::bitwise_and_n_ints, type.slotCount());
1908             break;
1909 
1910         case OperatorKind::LOGICALOR:
1911         case OperatorKind::BITWISEOR:
1912             // For logical-or, we verified above that the RHS does not have side effects.
1913             fBuilder.binary_op(BuilderOp::bitwise_or_n_ints, type.slotCount());
1914             break;
1915 
1916         case OperatorKind::LOGICALXOR:
1917         case OperatorKind::BITWISEXOR:
1918             // Logical-xor does not short circuit.
1919             fBuilder.binary_op(BuilderOp::bitwise_xor_n_ints, type.slotCount());
1920             break;
1921 
1922         default:
1923             return unsupported();
1924     }
1925 
1926     // If we have an lvalue, we need to write the result back into it.
1927     return lvalue ? this->store(*lvalue)
1928                   : true;
1929 }
1930 
pushConstructorCompound(const AnyConstructor & c)1931 bool Generator::pushConstructorCompound(const AnyConstructor& c) {
1932     for (const std::unique_ptr<Expression> &arg : c.argumentSpan()) {
1933         if (!this->pushExpression(*arg)) {
1934             return unsupported();
1935         }
1936     }
1937     return true;
1938 }
1939 
pushChildCall(const ChildCall & c)1940 bool Generator::pushChildCall(const ChildCall& c) {
1941     int* childIdx = fChildEffectMap.find(&c.child());
1942     SkASSERT(childIdx != nullptr);
1943     SkASSERT(!c.arguments().empty());
1944 
1945     // Save the dst.rgba fields; these hold our execution masks, and could potentially be
1946     // clobbered by the child effect.
1947     fBuilder.push_dst_rgba();
1948 
1949     // All child calls have at least one argument.
1950     const Expression* arg = c.arguments()[0].get();
1951     if (!this->pushExpression(*arg)) {
1952         return unsupported();
1953     }
1954 
1955     // Copy arguments from the stack into src/dst as required by this particular child-call.
1956     switch (c.child().type().typeKind()) {
1957         case Type::TypeKind::kShader: {
1958             // The argument must be a float2.
1959             SkASSERT(c.arguments().size() == 1);
1960             SkASSERT(arg->type().matches(*fProgram.fContext->fTypes.fFloat2));
1961             fBuilder.pop_src_rg();
1962             fBuilder.invoke_shader(*childIdx);
1963             break;
1964         }
1965         case Type::TypeKind::kColorFilter: {
1966             // The argument must be a half4/float4.
1967             SkASSERT(c.arguments().size() == 1);
1968             SkASSERT(arg->type().matches(*fProgram.fContext->fTypes.fHalf4) ||
1969                      arg->type().matches(*fProgram.fContext->fTypes.fFloat4));
1970             fBuilder.pop_src_rgba();
1971             fBuilder.invoke_color_filter(*childIdx);
1972             break;
1973         }
1974         case Type::TypeKind::kBlender: {
1975             // The first argument must be a half4/float4.
1976             SkASSERT(c.arguments().size() == 2);
1977             SkASSERT(arg->type().matches(*fProgram.fContext->fTypes.fHalf4) ||
1978                      arg->type().matches(*fProgram.fContext->fTypes.fFloat4));
1979 
1980             // The second argument must also be a half4/float4.
1981             arg = c.arguments()[1].get();
1982             SkASSERT(arg->type().matches(*fProgram.fContext->fTypes.fHalf4) ||
1983                      arg->type().matches(*fProgram.fContext->fTypes.fFloat4));
1984 
1985             if (!this->pushExpression(*arg)) {
1986                 return unsupported();
1987             }
1988 
1989             fBuilder.pop_dst_rgba();
1990             fBuilder.pop_src_rgba();
1991             fBuilder.invoke_blender(*childIdx);
1992             break;
1993         }
1994         default: {
1995             SkDEBUGFAILF("cannot sample from type '%s'", c.child().type().description().c_str());
1996         }
1997     }
1998 
1999     // Restore dst.rgba so our execution masks are back to normal.
2000     fBuilder.pop_dst_rgba();
2001 
2002     // The child call has returned the result color via src.rgba; push it onto the stack.
2003     fBuilder.push_src_rgba();
2004     return true;
2005 }
2006 
pushConstructorCast(const AnyConstructor & c)2007 bool Generator::pushConstructorCast(const AnyConstructor& c) {
2008     SkASSERT(c.argumentSpan().size() == 1);
2009     const Expression& inner = *c.argumentSpan().front();
2010     SkASSERT(inner.type().slotCount() == c.type().slotCount());
2011 
2012     if (!this->pushExpression(inner)) {
2013         return unsupported();
2014     }
2015     if (inner.type().componentType().numberKind() == c.type().componentType().numberKind()) {
2016         // Since we ignore type precision, this cast is effectively a no-op.
2017         return true;
2018     }
2019     if (inner.type().componentType().isSigned() && c.type().componentType().isUnsigned()) {
2020         // Treat uint(int) as a no-op.
2021         return true;
2022     }
2023     if (inner.type().componentType().isUnsigned() && c.type().componentType().isSigned()) {
2024         // Treat int(uint) as a no-op.
2025         return true;
2026     }
2027 
2028     if (c.type().componentType().isBoolean()) {
2029         // Converting int or float to boolean can be accomplished via `notEqual(x, 0)`.
2030         fBuilder.push_zeros(c.type().slotCount());
2031         return this->binaryOp(inner.type(), kNotEqualOps);
2032     }
2033     if (inner.type().componentType().isBoolean()) {
2034         // Converting boolean to int or float can be accomplished via bitwise-and.
2035         if (c.type().componentType().isFloat()) {
2036             fBuilder.push_literal_f(1.0f);
2037         } else if (c.type().componentType().isSigned() || c.type().componentType().isUnsigned()) {
2038             fBuilder.push_literal_i(1);
2039         } else {
2040             SkDEBUGFAILF("unexpected cast from bool to %s", c.type().description().c_str());
2041             return unsupported();
2042         }
2043         fBuilder.push_duplicates(c.type().slotCount() - 1);
2044         fBuilder.binary_op(BuilderOp::bitwise_and_n_ints, c.type().slotCount());
2045         return true;
2046     }
2047     // We have dedicated ops to cast between float and integer types.
2048     if (inner.type().componentType().isFloat()) {
2049         if (c.type().componentType().isSigned()) {
2050             fBuilder.unary_op(BuilderOp::cast_to_int_from_float, c.type().slotCount());
2051             return true;
2052         }
2053         if (c.type().componentType().isUnsigned()) {
2054             fBuilder.unary_op(BuilderOp::cast_to_uint_from_float, c.type().slotCount());
2055             return true;
2056         }
2057     } else if (c.type().componentType().isFloat()) {
2058         if (inner.type().componentType().isSigned()) {
2059             fBuilder.unary_op(BuilderOp::cast_to_float_from_int, c.type().slotCount());
2060             return true;
2061         }
2062         if (inner.type().componentType().isUnsigned()) {
2063             fBuilder.unary_op(BuilderOp::cast_to_float_from_uint, c.type().slotCount());
2064             return true;
2065         }
2066     }
2067 
2068     SkDEBUGFAILF("unexpected cast from %s to %s",
2069                  c.type().description().c_str(), inner.type().description().c_str());
2070     return unsupported();
2071 }
2072 
pushConstructorDiagonalMatrix(const ConstructorDiagonalMatrix & c)2073 bool Generator::pushConstructorDiagonalMatrix(const ConstructorDiagonalMatrix& c) {
2074     fBuilder.push_zeros(1);
2075     if (!this->pushExpression(*c.argument())) {
2076         return unsupported();
2077     }
2078     fBuilder.diagonal_matrix(c.type().columns(), c.type().rows());
2079 
2080     return true;
2081 }
2082 
pushConstructorMatrixResize(const ConstructorMatrixResize & c)2083 bool Generator::pushConstructorMatrixResize(const ConstructorMatrixResize& c) {
2084     if (!this->pushExpression(*c.argument())) {
2085         return unsupported();
2086     }
2087     fBuilder.matrix_resize(c.argument()->type().columns(),
2088                            c.argument()->type().rows(),
2089                            c.type().columns(),
2090                            c.type().rows());
2091     return true;
2092 }
2093 
pushConstructorSplat(const ConstructorSplat & c)2094 bool Generator::pushConstructorSplat(const ConstructorSplat& c) {
2095     if (!this->pushExpression(*c.argument())) {
2096         return unsupported();
2097     }
2098     fBuilder.push_duplicates(c.type().slotCount() - 1);
2099     return true;
2100 }
2101 
pushFieldAccess(const FieldAccess & f)2102 bool Generator::pushFieldAccess(const FieldAccess& f) {
2103     // If possible, get direct field access via the lvalue.
2104     std::unique_ptr<LValue> lvalue = this->makeLValue(f, /*allowScratch=*/true);
2105     return lvalue && this->push(*lvalue);
2106 }
2107 
pushFunctionCall(const FunctionCall & c)2108 bool Generator::pushFunctionCall(const FunctionCall& c) {
2109     if (c.function().isIntrinsic()) {
2110         return this->pushIntrinsic(c);
2111     }
2112 
2113     // Keep track of the current function.
2114     const FunctionDefinition* lastFunction = fCurrentFunction;
2115     fCurrentFunction = c.function().definition();
2116 
2117     // Skip over the function body entirely if there are no active lanes.
2118     // (If the function call was trivial, it would likely have been inlined in the frontend, so this
2119     // is likely to save a significant amount of work if the lanes are all dead.)
2120     int skipLabelID = fBuilder.nextLabelID();
2121     fBuilder.branch_if_no_active_lanes(skipLabelID);
2122 
2123     // Save off the return mask.
2124     if (this->needsReturnMask()) {
2125         fBuilder.enableExecutionMaskWrites();
2126         fBuilder.push_return_mask();
2127     }
2128 
2129     // Write all the arguments into their parameter's variable slots. Because we never allow
2130     // recursion, we don't need to worry about overwriting any existing values in those slots.
2131     // (In fact, we don't even need to apply the write mask.)
2132     SkTArray<std::unique_ptr<LValue>> lvalues;
2133     lvalues.resize(c.arguments().size());
2134 
2135     for (int index = 0; index < c.arguments().size(); ++index) {
2136         const Expression& arg = *c.arguments()[index];
2137         const Variable& param = *c.function().parameters()[index];
2138 
2139         // Use LValues for out-parameters and inout-parameters, so we can store back to them later.
2140         if (IsInoutParameter(param) || IsOutParameter(param)) {
2141             lvalues[index] = this->makeLValue(arg);
2142             if (!lvalues[index]) {
2143                 return unsupported();
2144             }
2145             // There are no guarantees on the starting value of an out-parameter, so we only need to
2146             // store the lvalues associated with an inout parameter.
2147             if (IsInoutParameter(param)) {
2148                 if (!this->push(*lvalues[index])) {
2149                     return unsupported();
2150                 }
2151                 this->popToSlotRangeUnmasked(this->getVariableSlots(param));
2152             }
2153         } else {
2154             // Copy input arguments into their respective parameter slots.
2155             if (!this->pushExpression(arg)) {
2156                 return unsupported();
2157             }
2158             this->popToSlotRangeUnmasked(this->getVariableSlots(param));
2159         }
2160     }
2161 
2162     // Emit the function body.
2163     std::optional<SlotRange> r = this->writeFunction(c, *fCurrentFunction);
2164     if (!r.has_value()) {
2165         return unsupported();
2166     }
2167 
2168     // Restore the original return mask.
2169     if (this->needsReturnMask()) {
2170         fBuilder.pop_return_mask();
2171         fBuilder.disableExecutionMaskWrites();
2172     }
2173 
2174     // We've returned back to the last function.
2175     fCurrentFunction = lastFunction;
2176 
2177     // Copy out-parameters and inout-parameters back to their homes.
2178     for (int index = 0; index < c.arguments().size(); ++index) {
2179         if (lvalues[index]) {
2180             // Only out- and inout-parameters should have an associated lvalue.
2181             const Variable& param = *c.function().parameters()[index];
2182             SkASSERT(IsInoutParameter(param) || IsOutParameter(param));
2183 
2184             // Copy the parameter's slots directly into the lvalue.
2185             fBuilder.push_slots(this->getVariableSlots(param));
2186             if (!this->store(*lvalues[index])) {
2187                 return unsupported();
2188             }
2189             this->discardExpression(param.type().slotCount());
2190         }
2191     }
2192 
2193     // Copy the function result from its slots onto the stack.
2194     fBuilder.push_slots(*r);
2195     fBuilder.label(skipLabelID);
2196     return true;
2197 }
2198 
pushIndexExpression(const IndexExpression & i)2199 bool Generator::pushIndexExpression(const IndexExpression& i) {
2200     std::unique_ptr<LValue> lvalue = this->makeLValue(i, /*allowScratch=*/true);
2201     return lvalue && this->push(*lvalue);
2202 }
2203 
pushIntrinsic(const FunctionCall & c)2204 bool Generator::pushIntrinsic(const FunctionCall& c) {
2205     const ExpressionArray& args = c.arguments();
2206     switch (args.size()) {
2207         case 1:
2208             return this->pushIntrinsic(c.function().intrinsicKind(), *args[0]);
2209 
2210         case 2:
2211             return this->pushIntrinsic(c.function().intrinsicKind(), *args[0], *args[1]);
2212 
2213         case 3:
2214             return this->pushIntrinsic(c.function().intrinsicKind(), *args[0], *args[1], *args[2]);
2215 
2216         default:
2217             break;
2218     }
2219 
2220     return unsupported();
2221 }
2222 
pushVectorizedExpression(const Expression & expr,const Type & vectorType)2223 bool Generator::pushVectorizedExpression(const Expression& expr, const Type& vectorType) {
2224     if (!this->pushExpression(expr)) {
2225         return unsupported();
2226     }
2227     if (vectorType.slotCount() > expr.type().slotCount()) {
2228         SkASSERT(expr.type().slotCount() == 1);
2229         fBuilder.push_duplicates(vectorType.slotCount() - expr.type().slotCount());
2230     }
2231     return true;
2232 }
2233 
pushIntrinsic(const TypedOps & ops,const Expression & arg0)2234 bool Generator::pushIntrinsic(const TypedOps& ops, const Expression& arg0) {
2235     if (!this->pushExpression(arg0)) {
2236         return unsupported();
2237     }
2238     return this->unaryOp(arg0.type(), ops);
2239 }
2240 
pushIntrinsic(BuilderOp builderOp,const Expression & arg0)2241 bool Generator::pushIntrinsic(BuilderOp builderOp, const Expression& arg0) {
2242     if (!this->pushExpression(arg0)) {
2243         return unsupported();
2244     }
2245     fBuilder.unary_op(builderOp, arg0.type().slotCount());
2246     return true;
2247 }
2248 
pushIntrinsic(IntrinsicKind intrinsic,const Expression & arg0)2249 bool Generator::pushIntrinsic(IntrinsicKind intrinsic, const Expression& arg0) {
2250     switch (intrinsic) {
2251         case IntrinsicKind::k_abs_IntrinsicKind:
2252             return this->pushIntrinsic(kAbsOps, arg0);
2253 
2254         case IntrinsicKind::k_any_IntrinsicKind:
2255             if (!this->pushExpression(arg0)) {
2256                 return unsupported();
2257             }
2258             this->foldWithMultiOp(BuilderOp::bitwise_or_n_ints, arg0.type().slotCount());
2259             return true;
2260 
2261         case IntrinsicKind::k_all_IntrinsicKind:
2262             if (!this->pushExpression(arg0)) {
2263                 return unsupported();
2264             }
2265             this->foldWithMultiOp(BuilderOp::bitwise_and_n_ints, arg0.type().slotCount());
2266             return true;
2267 
2268         case IntrinsicKind::k_atan_IntrinsicKind:
2269             return this->pushIntrinsic(BuilderOp::atan_float, arg0);
2270 
2271         case IntrinsicKind::k_ceil_IntrinsicKind:
2272             return this->pushIntrinsic(BuilderOp::ceil_float, arg0);
2273 
2274         case IntrinsicKind::k_cos_IntrinsicKind:
2275             return this->pushIntrinsic(BuilderOp::cos_float, arg0);
2276 
2277         case IntrinsicKind::k_degrees_IntrinsicKind: {
2278             Literal lit180OverPi{Position{}, 57.2957795131f, &arg0.type().componentType()};
2279             return this->pushBinaryExpression(arg0, OperatorKind::STAR, lit180OverPi);
2280         }
2281         case IntrinsicKind::k_floatBitsToInt_IntrinsicKind:
2282         case IntrinsicKind::k_floatBitsToUint_IntrinsicKind:
2283         case IntrinsicKind::k_intBitsToFloat_IntrinsicKind:
2284         case IntrinsicKind::k_uintBitsToFloat_IntrinsicKind:
2285             return this->pushExpression(arg0);
2286 
2287         case IntrinsicKind::k_exp_IntrinsicKind:
2288             return this->pushIntrinsic(BuilderOp::exp_float, arg0);
2289 
2290         case IntrinsicKind::k_floor_IntrinsicKind:
2291             return this->pushIntrinsic(BuilderOp::floor_float, arg0);
2292 
2293         case IntrinsicKind::k_fract_IntrinsicKind:
2294             // Implement fract as `x - floor(x)`.
2295             if (!this->pushExpression(arg0)) {
2296                 return unsupported();
2297             }
2298             fBuilder.push_clone(arg0.type().slotCount());
2299             fBuilder.unary_op(BuilderOp::floor_float, arg0.type().slotCount());
2300             return this->binaryOp(arg0.type(), kSubtractOps);
2301 
2302         case IntrinsicKind::k_length_IntrinsicKind:
2303             if (!this->pushExpression(arg0)) {
2304                 return unsupported();
2305             }
2306             // Implement length as `sqrt(dot(x, x))`.
2307             if (arg0.type().slotCount() > 1) {
2308                 fBuilder.push_clone(arg0.type().slotCount());
2309                 fBuilder.dot_floats(arg0.type().slotCount());
2310                 fBuilder.unary_op(BuilderOp::sqrt_float, 1);
2311             } else {
2312                 // The length of a scalar is `sqrt(x^2)`, which is equivalent to `abs(x)`.
2313                 fBuilder.unary_op(BuilderOp::abs_float, 1);
2314             }
2315             return true;
2316 
2317         case IntrinsicKind::k_not_IntrinsicKind:
2318             return this->pushPrefixExpression(OperatorKind::LOGICALNOT, arg0);
2319 
2320         case IntrinsicKind::k_radians_IntrinsicKind: {
2321             Literal litPiOver180{Position{}, 0.01745329251f, &arg0.type().componentType()};
2322             return this->pushBinaryExpression(arg0, OperatorKind::STAR, litPiOver180);
2323         }
2324         case IntrinsicKind::k_saturate_IntrinsicKind: {
2325             // Implement saturate as clamp(arg, 0, 1).
2326             Literal zeroLiteral{Position{}, 0.0, &arg0.type().componentType()};
2327             Literal oneLiteral{Position{}, 1.0, &arg0.type().componentType()};
2328             return this->pushIntrinsic(k_clamp_IntrinsicKind, arg0, zeroLiteral, oneLiteral);
2329         }
2330         case IntrinsicKind::k_sign_IntrinsicKind: {
2331             // Implement floating-point sign() as `clamp(arg * FLT_MAX, -1, 1)`.
2332             // FLT_MIN * FLT_MAX evaluates to 4, so multiplying any float value against FLT_MAX is
2333             // sufficient to ensure that |value| is always 1 or greater (excluding zero and nan).
2334             // Integer sign() doesn't need to worry about fractional values or nans, and can simply
2335             // be `clamp(arg, -1, 1)`.
2336             if (!this->pushExpression(arg0)) {
2337                 return unsupported();
2338             }
2339             if (arg0.type().componentType().isFloat()) {
2340                 Literal fltMaxLiteral{Position{}, FLT_MAX, &arg0.type().componentType()};
2341                 if (!this->pushVectorizedExpression(fltMaxLiteral, arg0.type())) {
2342                     return unsupported();
2343                 }
2344                 if (!this->binaryOp(arg0.type(), kMultiplyOps)) {
2345                     return unsupported();
2346                 }
2347             }
2348             Literal neg1Literal{Position{}, -1.0, &arg0.type().componentType()};
2349             if (!this->pushVectorizedExpression(neg1Literal, arg0.type())) {
2350                 return unsupported();
2351             }
2352             if (!this->binaryOp(arg0.type(), kMaxOps)) {
2353                 return unsupported();
2354             }
2355             Literal pos1Literal{Position{}, 1.0, &arg0.type().componentType()};
2356             if (!this->pushVectorizedExpression(pos1Literal, arg0.type())) {
2357                 return unsupported();
2358             }
2359             return this->binaryOp(arg0.type(), kMinOps);
2360         }
2361         case IntrinsicKind::k_sin_IntrinsicKind:
2362             return this->pushIntrinsic(BuilderOp::sin_float, arg0);
2363 
2364         case IntrinsicKind::k_sqrt_IntrinsicKind:
2365             return this->pushIntrinsic(BuilderOp::sqrt_float, arg0);
2366 
2367         case IntrinsicKind::k_tan_IntrinsicKind:
2368             return this->pushIntrinsic(BuilderOp::tan_float, arg0);
2369 
2370         case IntrinsicKind::k_transpose_IntrinsicKind:
2371             SkASSERT(arg0.type().isMatrix());
2372             if (!this->pushExpression(arg0)) {
2373                 return unsupported();
2374             }
2375             fBuilder.transpose(arg0.type().columns(), arg0.type().rows());
2376             return true;
2377 
2378         default:
2379             break;
2380     }
2381     return unsupported();
2382 }
2383 
pushIntrinsic(const TypedOps & ops,const Expression & arg0,const Expression & arg1)2384 bool Generator::pushIntrinsic(const TypedOps& ops, const Expression& arg0, const Expression& arg1) {
2385     if (!this->pushExpression(arg0) || !this->pushVectorizedExpression(arg1, arg0.type())) {
2386         return unsupported();
2387     }
2388     return this->binaryOp(arg0.type(), ops);
2389 }
2390 
pushIntrinsic(BuilderOp builderOp,const Expression & arg0,const Expression & arg1)2391 bool Generator::pushIntrinsic(BuilderOp builderOp, const Expression& arg0, const Expression& arg1) {
2392     if (!this->pushExpression(arg0) || !this->pushVectorizedExpression(arg1, arg0.type())) {
2393         return unsupported();
2394     }
2395     fBuilder.binary_op(builderOp, arg0.type().slotCount());
2396     return true;
2397 }
2398 
pushIntrinsic(IntrinsicKind intrinsic,const Expression & arg0,const Expression & arg1)2399 bool Generator::pushIntrinsic(IntrinsicKind intrinsic,
2400                               const Expression& arg0,
2401                               const Expression& arg1) {
2402     switch (intrinsic) {
2403         case IntrinsicKind::k_atan_IntrinsicKind:
2404             return this->pushIntrinsic(BuilderOp::atan2_n_floats, arg0, arg1);
2405 
2406         case IntrinsicKind::k_cross_IntrinsicKind: {
2407             // Implement cross as `arg0.yzx * arg1.zxy - arg0.zxy * arg1.yzx`. We use two stacks so
2408             // that each subexpression can be multiplied separately.
2409             SkASSERT(arg0.type().matches(arg1.type()));
2410             SkASSERT(arg0.type().slotCount() == 3);
2411             SkASSERT(arg1.type().slotCount() == 3);
2412 
2413             // Push `arg0.yzx` onto this stack and `arg0.zxy` onto a separate subexpression stack.
2414             AutoStack subexpressionStack(this);
2415             subexpressionStack.enter();
2416             if (!this->pushExpression(arg0)) {
2417                 return unsupported();
2418             }
2419             subexpressionStack.exit();
2420             subexpressionStack.pushClone(3);
2421 
2422             fBuilder.swizzle(/*consumedSlots=*/3, {1, 2, 0});
2423             subexpressionStack.enter();
2424             fBuilder.swizzle(/*consumedSlots=*/3, {2, 0, 1});
2425             subexpressionStack.exit();
2426 
2427             // Push `arg1.zxy` onto this stack and `arg1.yzx` onto the next stack. Perform the
2428             // multiply on each subexpression (`arg0.yzx * arg1.zxy` on the first stack, and
2429             // `arg0.zxy * arg1.yzx` on the next).
2430             subexpressionStack.enter();
2431             if (!this->pushExpression(arg1)) {
2432                 return unsupported();
2433             }
2434             subexpressionStack.exit();
2435             subexpressionStack.pushClone(3);
2436 
2437             fBuilder.swizzle(/*consumedSlots=*/3, {2, 0, 1});
2438             fBuilder.binary_op(BuilderOp::mul_n_floats, 3);
2439 
2440             subexpressionStack.enter();
2441             fBuilder.swizzle(/*consumedSlots=*/3, {1, 2, 0});
2442             fBuilder.binary_op(BuilderOp::mul_n_floats, 3);
2443             subexpressionStack.exit();
2444 
2445             // Migrate the result of the second subexpression (`arg0.zxy * arg1.yzx`) back onto the
2446             // main stack and subtract it from the first subexpression (`arg0.yzx * arg1.zxy`).
2447             subexpressionStack.pushClone(3);
2448             fBuilder.binary_op(BuilderOp::sub_n_floats, 3);
2449 
2450             // Now that the calculation is complete, discard the subexpression on the next stack.
2451             subexpressionStack.enter();
2452             this->discardExpression(/*slots=*/3);
2453             subexpressionStack.exit();
2454             return true;
2455         }
2456         case IntrinsicKind::k_dot_IntrinsicKind:
2457             // Implement dot as `a*b`, followed by folding via addition.
2458             SkASSERT(arg0.type().matches(arg1.type()));
2459             if (!this->pushExpression(arg0) || !this->pushExpression(arg1)) {
2460                 return unsupported();
2461             }
2462             fBuilder.dot_floats(arg0.type().slotCount());
2463             return true;
2464 
2465         case IntrinsicKind::k_equal_IntrinsicKind:
2466             SkASSERT(arg0.type().matches(arg1.type()));
2467             return this->pushIntrinsic(kEqualOps, arg0, arg1);
2468 
2469         case IntrinsicKind::k_notEqual_IntrinsicKind:
2470             SkASSERT(arg0.type().matches(arg1.type()));
2471             return this->pushIntrinsic(kNotEqualOps, arg0, arg1);
2472 
2473         case IntrinsicKind::k_lessThan_IntrinsicKind:
2474             SkASSERT(arg0.type().matches(arg1.type()));
2475             return this->pushIntrinsic(kLessThanOps, arg0, arg1);
2476 
2477         case IntrinsicKind::k_greaterThan_IntrinsicKind:
2478             SkASSERT(arg0.type().matches(arg1.type()));
2479             return this->pushIntrinsic(kLessThanOps, arg1, arg0);
2480 
2481         case IntrinsicKind::k_lessThanEqual_IntrinsicKind:
2482             SkASSERT(arg0.type().matches(arg1.type()));
2483             return this->pushIntrinsic(kLessThanEqualOps, arg0, arg1);
2484 
2485         case IntrinsicKind::k_greaterThanEqual_IntrinsicKind:
2486             SkASSERT(arg0.type().matches(arg1.type()));
2487             return this->pushIntrinsic(kLessThanEqualOps, arg1, arg0);
2488 
2489         case IntrinsicKind::k_min_IntrinsicKind:
2490             SkASSERT(arg0.type().componentType().matches(arg1.type().componentType()));
2491             return this->pushIntrinsic(kMinOps, arg0, arg1);
2492 
2493         case IntrinsicKind::k_matrixCompMult_IntrinsicKind:
2494             SkASSERT(arg0.type().matches(arg1.type()));
2495             return this->pushIntrinsic(kMultiplyOps, arg0, arg1);
2496 
2497         case IntrinsicKind::k_max_IntrinsicKind:
2498             SkASSERT(arg0.type().componentType().matches(arg1.type().componentType()));
2499             return this->pushIntrinsic(kMaxOps, arg0, arg1);
2500 
2501         case IntrinsicKind::k_pow_IntrinsicKind:
2502             SkASSERT(arg0.type().matches(arg1.type()));
2503             return this->pushIntrinsic(BuilderOp::pow_n_floats, arg0, arg1);
2504 
2505         case IntrinsicKind::k_step_IntrinsicKind: {
2506             // Compute step as `float(lessThan(edge, x))`. We convert from boolean 0/~0 to floating
2507             // point zero/one by using a bitwise-and against the bit-pattern of 1.0.
2508             SkASSERT(arg0.type().componentType().matches(arg1.type().componentType()));
2509             if (!this->pushVectorizedExpression(arg0, arg1.type()) || !this->pushExpression(arg1)) {
2510                 return unsupported();
2511             }
2512             if (!this->binaryOp(arg1.type(), kLessThanOps)) {
2513                 return unsupported();
2514             }
2515             Literal pos1Literal{Position{}, 1.0, &arg1.type().componentType()};
2516             if (!this->pushVectorizedExpression(pos1Literal, arg1.type())) {
2517                 return unsupported();
2518             }
2519             fBuilder.binary_op(BuilderOp::bitwise_and_n_ints, arg1.type().slotCount());
2520             return true;
2521         }
2522 
2523         default:
2524             break;
2525     }
2526     return unsupported();
2527 }
2528 
pushIntrinsic(IntrinsicKind intrinsic,const Expression & arg0,const Expression & arg1,const Expression & arg2)2529 bool Generator::pushIntrinsic(IntrinsicKind intrinsic,
2530                               const Expression& arg0,
2531                               const Expression& arg1,
2532                               const Expression& arg2) {
2533     switch (intrinsic) {
2534         case IntrinsicKind::k_clamp_IntrinsicKind:
2535             // Implement clamp as min(max(arg, low), high).
2536             SkASSERT(arg0.type().componentType().matches(arg1.type().componentType()));
2537             SkASSERT(arg0.type().componentType().matches(arg2.type().componentType()));
2538             if (!this->pushExpression(arg0) || !this->pushVectorizedExpression(arg1, arg0.type())) {
2539                 return unsupported();
2540             }
2541             if (!this->binaryOp(arg0.type(), kMaxOps)) {
2542                 return unsupported();
2543             }
2544             if (!this->pushVectorizedExpression(arg2, arg0.type())) {
2545                 return unsupported();
2546             }
2547             if (!this->binaryOp(arg0.type(), kMinOps)) {
2548                 return unsupported();
2549             }
2550             return true;
2551 
2552         case IntrinsicKind::k_mix_IntrinsicKind:
2553             // Note: our SkRP mix op takes the interpolation point first, not the interpolants.
2554             SkASSERT(arg0.type().matches(arg1.type()));
2555             if (arg2.type().componentType().isFloat()) {
2556                 SkASSERT(arg0.type().componentType().matches(arg2.type().componentType()));
2557                 if (!this->pushVectorizedExpression(arg2, arg0.type())) {
2558                     return unsupported();
2559                 }
2560                 if (!this->pushExpression(arg0) || !this->pushExpression(arg1)) {
2561                     return unsupported();
2562                 }
2563                 return this->ternaryOp(arg0.type(), kMixOps);
2564             }
2565             if (arg2.type().componentType().isBoolean()) {
2566                 if (!this->pushExpression(arg2)) {
2567                     return unsupported();
2568                 }
2569                 if (!this->pushExpression(arg0) || !this->pushExpression(arg1)) {
2570                     return unsupported();
2571                 }
2572                 // The `mix_int` op isn't doing a lerp; it uses the third argument to select values
2573                 // from the first and second arguments. It's safe for use with any type in arguments
2574                 // 0 and 1.
2575                 fBuilder.ternary_op(BuilderOp::mix_n_ints, arg0.type().slotCount());
2576                 return true;
2577             }
2578             return unsupported();
2579 
2580         default:
2581             break;
2582     }
2583     return unsupported();
2584 }
2585 
pushLiteral(const Literal & l)2586 bool Generator::pushLiteral(const Literal& l) {
2587     switch (l.type().numberKind()) {
2588         case Type::NumberKind::kFloat:
2589             fBuilder.push_literal_f(l.floatValue());
2590             return true;
2591 
2592         case Type::NumberKind::kSigned:
2593             fBuilder.push_literal_i(l.intValue());
2594             return true;
2595 
2596         case Type::NumberKind::kUnsigned:
2597             fBuilder.push_literal_u(l.intValue());
2598             return true;
2599 
2600         case Type::NumberKind::kBoolean:
2601             fBuilder.push_literal_i(l.boolValue() ? ~0 : 0);
2602             return true;
2603 
2604         default:
2605             SkUNREACHABLE;
2606     }
2607 }
2608 
pushPostfixExpression(const PostfixExpression & p,bool usesResult)2609 bool Generator::pushPostfixExpression(const PostfixExpression& p, bool usesResult) {
2610     // If the result is ignored...
2611     if (!usesResult) {
2612         // ... just emit a prefix expression instead.
2613         return this->pushPrefixExpression(p.getOperator(), *p.operand());
2614     }
2615     // Get the operand as an lvalue, and push it onto the stack as-is.
2616     std::unique_ptr<LValue> lvalue = this->makeLValue(*p.operand());
2617     if (!lvalue || !this->push(*lvalue)) {
2618         return unsupported();
2619     }
2620 
2621     // Push a scratch copy of the operand.
2622     fBuilder.push_clone(p.type().slotCount());
2623 
2624     // Increment or decrement the scratch copy by one.
2625     Literal oneLiteral{Position{}, 1.0, &p.type().componentType()};
2626     if (!this->pushVectorizedExpression(oneLiteral, p.type())) {
2627         return unsupported();
2628     }
2629 
2630     switch (p.getOperator().kind()) {
2631         case OperatorKind::PLUSPLUS:
2632             if (!this->binaryOp(p.type(), kAddOps)) {
2633                 return unsupported();
2634             }
2635             break;
2636 
2637         case OperatorKind::MINUSMINUS:
2638             if (!this->binaryOp(p.type(), kSubtractOps)) {
2639                 return unsupported();
2640             }
2641             break;
2642 
2643         default:
2644             SkUNREACHABLE;
2645     }
2646 
2647     // Write the new value back to the operand.
2648     if (!this->store(*lvalue)) {
2649         return unsupported();
2650     }
2651 
2652     // Discard the scratch copy, leaving only the original value as-is.
2653     this->discardExpression(p.type().slotCount());
2654     return true;
2655 }
2656 
pushPrefixExpression(const PrefixExpression & p)2657 bool Generator::pushPrefixExpression(const PrefixExpression& p) {
2658     return this->pushPrefixExpression(p.getOperator(), *p.operand());
2659 }
2660 
pushPrefixExpression(Operator op,const Expression & expr)2661 bool Generator::pushPrefixExpression(Operator op, const Expression& expr) {
2662     switch (op.kind()) {
2663         case OperatorKind::BITWISENOT:
2664         case OperatorKind::LOGICALNOT:
2665             // Handle operators ! and ~.
2666             if (!this->pushExpression(expr)) {
2667                 return unsupported();
2668             }
2669             fBuilder.unary_op(BuilderOp::bitwise_not_int, expr.type().slotCount());
2670             return true;
2671 
2672         case OperatorKind::MINUS:
2673             // Handle negation as a componentwise `0 - expr`.
2674             fBuilder.push_zeros(expr.type().slotCount());
2675             if (!this->pushExpression(expr)) {
2676                 return unsupported();
2677             }
2678             return this->binaryOp(expr.type(), kSubtractOps);
2679 
2680         case OperatorKind::PLUSPLUS: {
2681             // Rewrite as `expr += 1`.
2682             Literal oneLiteral{Position{}, 1.0, &expr.type().componentType()};
2683             return this->pushBinaryExpression(expr, OperatorKind::PLUSEQ, oneLiteral);
2684         }
2685         case OperatorKind::MINUSMINUS: {
2686             // Rewrite as `expr -= 1`.
2687             Literal oneLiteral{Position{}, 1.0, &expr.type().componentType()};
2688             return this->pushBinaryExpression(expr, OperatorKind::MINUSEQ, oneLiteral);
2689         }
2690         default:
2691             break;
2692     }
2693 
2694     return unsupported();
2695 }
2696 
pushSwizzle(const Swizzle & s)2697 bool Generator::pushSwizzle(const Swizzle& s) {
2698     SkASSERT(!s.components().empty() && s.components().size() <= 4);
2699 
2700     // If this is a simple subset of a variable's slots...
2701     bool isSimpleSubset = is_sliceable_swizzle(s.components());
2702     if (isSimpleSubset && s.base()->is<VariableReference>()) {
2703         // ... we can just push part of the variable directly onto the stack, rather than pushing
2704         // the whole expression and then immediately cutting it down. (Either way works, but this
2705         // saves a step.)
2706         return this->pushVariableReferencePartial(
2707                 s.base()->as<VariableReference>(),
2708                 SlotRange{/*index=*/s.components()[0], /*count=*/s.components().size()});
2709     }
2710     // Push the base expression.
2711     if (!this->pushExpression(*s.base())) {
2712         return false;
2713     }
2714     // An identity swizzle doesn't rearrange the data; it just (potentially) discards tail elements.
2715     if (isSimpleSubset && s.components()[0] == 0) {
2716         int discardedElements = s.base()->type().slotCount() - s.components().size();
2717         SkASSERT(discardedElements >= 0);
2718         fBuilder.discard_stack(discardedElements);
2719         return true;
2720     }
2721     // Perform the swizzle.
2722     fBuilder.swizzle(s.base()->type().slotCount(), s.components());
2723     return true;
2724 }
2725 
pushTernaryExpression(const TernaryExpression & t)2726 bool Generator::pushTernaryExpression(const TernaryExpression& t) {
2727     return this->pushTernaryExpression(*t.test(), *t.ifTrue(), *t.ifFalse());
2728 }
2729 
pushDynamicallyUniformTernaryExpression(const Expression & test,const Expression & ifTrue,const Expression & ifFalse)2730 bool Generator::pushDynamicallyUniformTernaryExpression(const Expression& test,
2731                                                         const Expression& ifTrue,
2732                                                         const Expression& ifFalse) {
2733     SkASSERT(Analysis::IsDynamicallyUniformExpression(test));
2734 
2735     int falseLabelID = fBuilder.nextLabelID();
2736     int exitLabelID = fBuilder.nextLabelID();
2737 
2738     // First, push the test-expression into a separate stack.
2739     AutoStack testStack(this);
2740     testStack.enter();
2741     if (!this->pushExpression(test)) {
2742         return unsupported();
2743     }
2744 
2745     // Branch to the true- or false-expression based on the test-expression. We can skip the
2746     // non-true path entirely since the test is known to be uniform.
2747     fBuilder.branch_if_no_active_lanes_on_stack_top_equal(~0, falseLabelID);
2748     testStack.exit();
2749 
2750     if (!this->pushExpression(ifTrue)) {
2751         return unsupported();
2752     }
2753 
2754     fBuilder.jump(exitLabelID);
2755 
2756     // The builder doesn't understand control flow, and assumes that every push moves the stack-top
2757     // forwards. We need to manually balance out the `pushExpression` from the if-true path by
2758     // moving the stack position backwards, so that the if-false path pushes its expression into the
2759     // same as the if-true result.
2760     this->discardExpression(/*slots=*/ifTrue.type().slotCount());
2761 
2762     fBuilder.label(falseLabelID);
2763 
2764     if (!this->pushExpression(ifFalse)) {
2765         return unsupported();
2766     }
2767 
2768     fBuilder.label(exitLabelID);
2769 
2770     // Jettison the text-expression from the separate stack.
2771     testStack.enter();
2772     this->discardExpression(/*slots=*/1);
2773     testStack.exit();
2774     return true;
2775 }
2776 
pushTernaryExpression(const Expression & test,const Expression & ifTrue,const Expression & ifFalse)2777 bool Generator::pushTernaryExpression(const Expression& test,
2778                                       const Expression& ifTrue,
2779                                       const Expression& ifFalse) {
2780     // If the test-expression is dynamically-uniform, we can skip over the non-true expressions
2781     // entirely, and not need to involve the condition mask.
2782     if (Analysis::IsDynamicallyUniformExpression(test)) {
2783         return this->pushDynamicallyUniformTernaryExpression(test, ifTrue, ifFalse);
2784     }
2785 
2786     // Analyze the ternary to see which corners we can safely cut.
2787     bool ifFalseHasSideEffects = Analysis::HasSideEffects(ifFalse);
2788     bool ifTrueHasSideEffects  = Analysis::HasSideEffects(ifTrue);
2789     bool ifTrueIsTrivial       = Analysis::IsTrivialExpression(ifTrue);
2790     int  cleanupLabelID        = fBuilder.nextLabelID();
2791 
2792     // If the true- and false-expressions both lack side effects, we evaluate both of them safely
2793     // without masking off their effects. In that case, we can emit both sides and use boolean mix
2794     // to select the correct result without using the condition mask at all.
2795     if (!ifFalseHasSideEffects && !ifTrueHasSideEffects && ifTrueIsTrivial) {
2796         // Push all of the arguments to mix.
2797         if (!this->pushVectorizedExpression(test, ifTrue.type())) {
2798             return unsupported();
2799         }
2800         if (!this->pushExpression(ifFalse)) {
2801             return unsupported();
2802         }
2803         if (!this->pushExpression(ifTrue)) {
2804             return unsupported();
2805         }
2806         // Use boolean mix to select the true- or false-expression via the test-expression.
2807         fBuilder.ternary_op(BuilderOp::mix_n_ints, ifTrue.type().slotCount());
2808         return true;
2809     }
2810 
2811     // First, push the current condition-mask and the test-expression into a separate stack.
2812     fBuilder.enableExecutionMaskWrites();
2813     AutoStack testStack(this);
2814     testStack.enter();
2815     fBuilder.push_condition_mask();
2816     if (!this->pushExpression(test)) {
2817         return unsupported();
2818     }
2819     testStack.exit();
2820 
2821     // We can take some shortcuts with condition-mask handling if the false-expression is entirely
2822     // side-effect free. (We can evaluate it without masking off its effects.) We always handle the
2823     // condition mask properly for the test-expression and true-expression properly.
2824     if (!ifFalseHasSideEffects) {
2825         // Push the false-expression onto the primary stack.
2826         if (!this->pushExpression(ifFalse)) {
2827             return unsupported();
2828         }
2829 
2830         // Next, merge the condition mask (on the separate stack) with the test expression.
2831         testStack.enter();
2832         fBuilder.merge_condition_mask();
2833         testStack.exit();
2834 
2835         // If no lanes are active, we can skip the true-expression entirely. This isn't super likely
2836         // to happen, so it's probably only a win for non-trivial true-expressions.
2837         if (!ifTrueIsTrivial) {
2838             fBuilder.branch_if_no_active_lanes(cleanupLabelID);
2839         }
2840 
2841         // Push the true-expression onto the primary stack, immediately after the false-expression.
2842         if (!this->pushExpression(ifTrue)) {
2843             return unsupported();
2844         }
2845 
2846         // Use a select to conditionally mask-merge the true-expression and false-expression lanes.
2847         fBuilder.select(/*slots=*/ifTrue.type().slotCount());
2848         fBuilder.label(cleanupLabelID);
2849     } else {
2850         // Merge the condition mask (on the separate stack) with the test expression.
2851         testStack.enter();
2852         fBuilder.merge_condition_mask();
2853         testStack.exit();
2854 
2855         // Push the true-expression onto the primary stack.
2856         if (!this->pushExpression(ifTrue)) {
2857             return unsupported();
2858         }
2859 
2860         // Switch back to the test-expression stack temporarily, and negate the test condition.
2861         testStack.enter();
2862         fBuilder.unary_op(BuilderOp::bitwise_not_int, /*slots=*/1);
2863         fBuilder.merge_condition_mask();
2864         testStack.exit();
2865 
2866         // Push the false-expression onto the primary stack, immediately after the true-expression.
2867         if (!this->pushExpression(ifFalse)) {
2868             return unsupported();
2869         }
2870 
2871         // Use a select to conditionally mask-merge the true-expression and false-expression lanes;
2872         // the mask is already set up for this.
2873         fBuilder.select(/*slots=*/ifTrue.type().slotCount());
2874     }
2875 
2876     // Restore the condition-mask to its original state and jettison the test-expression.
2877     testStack.enter();
2878     this->discardExpression(/*slots=*/1);
2879     fBuilder.pop_condition_mask();
2880     testStack.exit();
2881 
2882     fBuilder.disableExecutionMaskWrites();
2883     return true;
2884 }
2885 
pushVariableReference(const VariableReference & v)2886 bool Generator::pushVariableReference(const VariableReference& v) {
2887     return this->pushVariableReferencePartial(v, SlotRange{0, (int)v.type().slotCount()});
2888 }
2889 
pushVariableReferencePartial(const VariableReference & v,SlotRange subset)2890 bool Generator::pushVariableReferencePartial(const VariableReference& v, SlotRange subset) {
2891     const Variable& var = *v.variable();
2892     SlotRange r;
2893     if (IsUniform(var)) {
2894         r = this->getUniformSlots(var);
2895         SkASSERT(r.count == (int)var.type().slotCount());
2896         r.index += subset.index;
2897         r.count = subset.count;
2898         fBuilder.push_uniform(r);
2899     } else {
2900         r = this->getVariableSlots(var);
2901         SkASSERT(r.count == (int)var.type().slotCount());
2902         r.index += subset.index;
2903         r.count = subset.count;
2904         fBuilder.push_slots(r);
2905     }
2906     return true;
2907 }
2908 
writeProgram(const FunctionDefinition & function)2909 bool Generator::writeProgram(const FunctionDefinition& function) {
2910     fCurrentFunction = &function;
2911 
2912     if (fDebugTrace) {
2913         // Copy the program source into the debug info so that it will be written in the trace file.
2914         fDebugTrace->setSource(*fProgram.fSource);
2915     }
2916     // Assign slots to the parameters of main; copy src and dst into those slots as appropriate.
2917     for (const SkSL::Variable* param : function.declaration().parameters()) {
2918         switch (param->modifiers().fLayout.fBuiltin) {
2919             case SK_MAIN_COORDS_BUILTIN: {
2920                 // Coordinates are passed via RG.
2921                 SlotRange fragCoord = this->getVariableSlots(*param);
2922                 SkASSERT(fragCoord.count == 2);
2923                 fBuilder.store_src_rg(fragCoord);
2924                 break;
2925             }
2926             case SK_INPUT_COLOR_BUILTIN: {
2927                 // Input colors are passed via RGBA.
2928                 SlotRange srcColor = this->getVariableSlots(*param);
2929                 SkASSERT(srcColor.count == 4);
2930                 fBuilder.store_src(srcColor);
2931                 break;
2932             }
2933             case SK_DEST_COLOR_BUILTIN: {
2934                 // Dest colors are passed via dRGBA.
2935                 SlotRange destColor = this->getVariableSlots(*param);
2936                 SkASSERT(destColor.count == 4);
2937                 fBuilder.store_dst(destColor);
2938                 break;
2939             }
2940             default: {
2941                 SkDEBUGFAIL("Invalid parameter to main()");
2942                 return unsupported();
2943             }
2944         }
2945     }
2946 
2947     // Initialize the program.
2948     fBuilder.init_lane_masks();
2949 
2950     // Emit global variables.
2951     if (!this->writeGlobals()) {
2952         return unsupported();
2953     }
2954 
2955     // Invoke main().
2956     if (this->needsReturnMask()) {
2957         fBuilder.enableExecutionMaskWrites();
2958     }
2959 
2960     std::optional<SlotRange> mainResult = this->writeFunction(function, function);
2961     if (!mainResult.has_value()) {
2962         return unsupported();
2963     }
2964 
2965     if (this->needsReturnMask()) {
2966         fBuilder.disableExecutionMaskWrites();
2967     }
2968 
2969     // Move the result of main() from slots into RGBA. Allow dRGBA to remain in a trashed state.
2970     SkASSERT(mainResult->count == 4);
2971     fBuilder.load_src(*mainResult);
2972     return true;
2973 }
2974 
finish()2975 std::unique_ptr<RP::Program> Generator::finish() {
2976     return fBuilder.finish(fProgramSlots.slotCount(), fUniformSlots.slotCount(), fDebugTrace);
2977 }
2978 
2979 }  // namespace RP
2980 
MakeRasterPipelineProgram(const SkSL::Program & program,const FunctionDefinition & function,SkRPDebugTrace * debugTrace)2981 std::unique_ptr<RP::Program> MakeRasterPipelineProgram(const SkSL::Program& program,
2982                                                        const FunctionDefinition& function,
2983                                                        SkRPDebugTrace* debugTrace) {
2984     RP::Generator generator(program, debugTrace);
2985     if (!generator.writeProgram(function)) {
2986         return nullptr;
2987     }
2988     return generator.finish();
2989 }
2990 
2991 }  // namespace SkSL
2992