Coverage Report

Created: 2024-05-20 07:14

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