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