Update rive-cpp to 2.0 version
[platform/core/uifw/rive-tizen.git] / submodule / skia / src / sksl / codegen / SkSLSPIRVCodeGenerator.h
1 /*
2  * Copyright 2016 Google Inc.
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 #ifndef SKSL_SPIRVCODEGENERATOR
9 #define SKSL_SPIRVCODEGENERATOR
10
11 #include "include/private/SkSLDefines.h"
12 #include "include/private/SkSLLayout.h"
13 #include "include/private/SkSLModifiers.h"
14 #include "include/private/SkSLProgramKind.h"
15 #include "include/private/SkTArray.h"
16 #include "include/private/SkTHash.h"
17 #include "src/sksl/SkSLMemoryLayout.h"
18 #include "src/sksl/SkSLStringStream.h"
19 #include "src/sksl/codegen/SkSLCodeGenerator.h"
20 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
21 #include "src/sksl/ir/SkSLFunctionDefinition.h"
22 #include "src/sksl/ir/SkSLInterfaceBlock.h"
23 #include "src/sksl/ir/SkSLSymbolTable.h"
24 #include "src/sksl/ir/SkSLType.h"
25 #include "src/sksl/ir/SkSLVariable.h"
26 #include "src/sksl/spirv.h"
27
28 #include <cstdint>
29 #include <memory>
30 #include <stack>
31 #include <string>
32 #include <string_view>
33 #include <vector>
34
35 template <typename T> class SkSpan;
36
37 namespace SkSL {
38
39 class AnyConstructor;
40 class BinaryExpression;
41 class Block;
42 class ConstructorCompound;
43 class ConstructorCompoundCast;
44 class ConstructorDiagonalMatrix;
45 class ConstructorMatrixResize;
46 class ConstructorScalarCast;
47 class ConstructorSplat;
48 class Context;
49 class DoStatement;
50 class Expression;
51 class FieldAccess;
52 class ForStatement;
53 class FunctionCall;
54 class IfStatement;
55 class Literal;
56 class Operator;
57 class OutputStream;
58 class Position;
59 class PostfixExpression;
60 class PrefixExpression;
61 class ProgramElement;
62 class ReturnStatement;
63 class Statement;
64 class SwitchStatement;
65 class TernaryExpression;
66 class VarDeclaration;
67 class VariableReference;
68 enum IntrinsicKind : int8_t;
69 struct IndexExpression;
70 struct Program;
71 struct Swizzle;
72
73 /**
74  * Converts a Program into a SPIR-V binary.
75  */
76 class SPIRVCodeGenerator : public CodeGenerator {
77 public:
78     // We reserve an impossible SpvId as a sentinel. (NA meaning none, n/a, etc.)
79     static constexpr SpvId NA = (SpvId)-1;
80
81     class LValue {
82     public:
83         virtual ~LValue() {}
84
85         // returns a pointer to the lvalue, if possible. If the lvalue cannot be directly referenced
86         // by a pointer (e.g. vector swizzles), returns NA.
87         virtual SpvId getPointer() { return NA; }
88
89         // Returns true if a valid pointer returned by getPointer represents a memory object
90         // (see https://github.com/KhronosGroup/SPIRV-Tools/issues/2892). Has no meaning if
91         // getPointer() returns NA.
92         virtual bool isMemoryObjectPointer() const { return true; }
93
94         // Applies a swizzle to the components of the LValue, if possible. This is used to create
95         // LValues that are swizzes-of-swizzles. Non-swizzle LValues can just return false.
96         virtual bool applySwizzle(const ComponentArray& components, const Type& newType) {
97             return false;
98         }
99
100         virtual SpvId load(OutputStream& out) = 0;
101
102         virtual void store(SpvId value, OutputStream& out) = 0;
103     };
104
105     SPIRVCodeGenerator(const Context* context,
106                        const Program* program,
107                        OutputStream* out)
108             : INHERITED(context, program, out)
109             , fDefaultLayout(MemoryLayout::k140_Standard)
110             , fCapabilities(0)
111             , fIdCount(1)
112             , fCurrentBlock(0)
113             , fSynthetics(fContext, /*builtin=*/true) {}
114
115     bool generateCode() override;
116
117 private:
118     enum IntrinsicOpcodeKind {
119         kGLSL_STD_450_IntrinsicOpcodeKind,
120         kSPIRV_IntrinsicOpcodeKind,
121         kSpecial_IntrinsicOpcodeKind,
122         kInvalid_IntrinsicOpcodeKind,
123     };
124
125     enum SpecialIntrinsic {
126         kAtan_SpecialIntrinsic,
127         kClamp_SpecialIntrinsic,
128         kMatrixCompMult_SpecialIntrinsic,
129         kMax_SpecialIntrinsic,
130         kMin_SpecialIntrinsic,
131         kMix_SpecialIntrinsic,
132         kMod_SpecialIntrinsic,
133         kDFdy_SpecialIntrinsic,
134         kSaturate_SpecialIntrinsic,
135         kSampledImage_SpecialIntrinsic,
136         kSmoothStep_SpecialIntrinsic,
137         kStep_SpecialIntrinsic,
138         kSubpassLoad_SpecialIntrinsic,
139         kTexture_SpecialIntrinsic,
140     };
141
142     enum class Precision {
143         kDefault,
144         kRelaxed,
145     };
146
147     struct TempVar {
148         SpvId spvId;
149         const Type* type;
150         std::unique_ptr<SPIRVCodeGenerator::LValue> lvalue;
151     };
152
153     /**
154      * Pass in the type to automatically add a RelaxedPrecision decoration for the id when
155      * appropriate, or null to never add one.
156      */
157     SpvId nextId(const Type* type);
158
159     SpvId nextId(Precision precision);
160
161     SpvId getType(const Type& type);
162
163     SpvId getType(const Type& type, const MemoryLayout& layout);
164
165     SpvId getFunctionType(const FunctionDeclaration& function);
166
167     SpvId getPointerType(const Type& type, SpvStorageClass_ storageClass);
168
169     SpvId getPointerType(const Type& type, const MemoryLayout& layout,
170                          SpvStorageClass_ storageClass);
171
172     std::vector<SpvId> getAccessChain(const Expression& expr, OutputStream& out);
173
174     void writeLayout(const Layout& layout, SpvId target, Position pos);
175
176     void writeFieldLayout(const Layout& layout, SpvId target, int member);
177
178     SpvId writeStruct(const Type& type, const MemoryLayout& memoryLayout);
179
180     void writeProgramElement(const ProgramElement& pe, OutputStream& out);
181
182     SpvId writeInterfaceBlock(const InterfaceBlock& intf, bool appendRTFlip = true);
183
184     SpvId writeFunctionStart(const FunctionDeclaration& f, OutputStream& out);
185
186     SpvId writeFunctionDeclaration(const FunctionDeclaration& f, OutputStream& out);
187
188     SpvId writeFunction(const FunctionDefinition& f, OutputStream& out);
189
190     void writeGlobalVar(ProgramKind kind, const VarDeclaration& v);
191
192     void writeVarDeclaration(const VarDeclaration& var, OutputStream& out);
193
194     SpvId writeVariableReference(const VariableReference& ref, OutputStream& out);
195
196     int findUniformFieldIndex(const Variable& var) const;
197
198     std::unique_ptr<LValue> getLValue(const Expression& value, OutputStream& out);
199
200     SpvId writeExpression(const Expression& expr, OutputStream& out);
201
202     SpvId writeIntrinsicCall(const FunctionCall& c, OutputStream& out);
203
204     SpvId writeFunctionCallArgument(const FunctionCall& call,
205                                     int argIndex,
206                                     std::vector<TempVar>* tempVars,
207                                     OutputStream& out);
208
209     void copyBackTempVars(const std::vector<TempVar>& tempVars, OutputStream& out);
210
211     SpvId writeFunctionCall(const FunctionCall& c, OutputStream& out);
212
213
214     void writeGLSLExtendedInstruction(const Type& type, SpvId id, SpvId floatInst,
215                                       SpvId signedInst, SpvId unsignedInst,
216                                       const std::vector<SpvId>& args, OutputStream& out);
217
218     /**
219      * Promotes an expression to a vector. If the expression is already a vector with vectorSize
220      * columns, returns it unmodified. If the expression is a scalar, either promotes it to a
221      * vector (if vectorSize > 1) or returns it unmodified (if vectorSize == 1). Asserts if the
222      * expression is already a vector and it does not have vectorSize columns.
223      */
224     SpvId vectorize(const Expression& expr, int vectorSize, OutputStream& out);
225
226     /**
227      * Given a list of potentially mixed scalars and vectors, promotes the scalars to match the
228      * size of the vectors and returns the ids of the written expressions. e.g. given (float, vec2),
229      * returns (vec2(float), vec2). It is an error to use mismatched vector sizes, e.g. (float,
230      * vec2, vec3).
231      */
232     std::vector<SpvId> vectorize(const ExpressionArray& args, OutputStream& out);
233
234     SpvId writeSpecialIntrinsic(const FunctionCall& c, SpecialIntrinsic kind, OutputStream& out);
235
236     SpvId writeScalarToMatrixSplat(const Type& matrixType, SpvId scalarId, OutputStream& out);
237
238     SpvId writeFloatConstructor(const AnyConstructor& c, OutputStream& out);
239
240     SpvId castScalarToFloat(SpvId inputId, const Type& inputType, const Type& outputType,
241                             OutputStream& out);
242
243     SpvId writeIntConstructor(const AnyConstructor& c, OutputStream& out);
244
245     SpvId castScalarToSignedInt(SpvId inputId, const Type& inputType, const Type& outputType,
246                                 OutputStream& out);
247
248     SpvId writeUIntConstructor(const AnyConstructor& c, OutputStream& out);
249
250     SpvId castScalarToUnsignedInt(SpvId inputId, const Type& inputType, const Type& outputType,
251                                   OutputStream& out);
252
253     SpvId writeBooleanConstructor(const AnyConstructor& c, OutputStream& out);
254
255     SpvId castScalarToBoolean(SpvId inputId, const Type& inputType, const Type& outputType,
256                               OutputStream& out);
257
258     SpvId castScalarToType(SpvId inputExprId, const Type& inputType, const Type& outputType,
259                            OutputStream& out);
260
261     /**
262      * Writes a potentially-different-sized copy of a matrix. Entries which do not exist in the
263      * source matrix are filled with zero; entries which do not exist in the destination matrix are
264      * ignored.
265      */
266     SpvId writeMatrixCopy(SpvId src, const Type& srcType, const Type& dstType, OutputStream& out);
267
268     void addColumnEntry(const Type& columnType, SkTArray<SpvId>* currentColumn,
269                         SkTArray<SpvId>* columnIds, int rows, SpvId entry, OutputStream& out);
270
271     SpvId writeConstructorCompound(const ConstructorCompound& c, OutputStream& out);
272
273     SpvId writeMatrixConstructor(const ConstructorCompound& c, OutputStream& out);
274
275     SpvId writeVectorConstructor(const ConstructorCompound& c, OutputStream& out);
276
277     SpvId writeCompositeConstructor(const AnyConstructor& c, OutputStream& out);
278
279     SpvId writeConstructorDiagonalMatrix(const ConstructorDiagonalMatrix& c, OutputStream& out);
280
281     SpvId writeConstructorMatrixResize(const ConstructorMatrixResize& c, OutputStream& out);
282
283     SpvId writeConstructorScalarCast(const ConstructorScalarCast& c, OutputStream& out);
284
285     SpvId writeConstructorSplat(const ConstructorSplat& c, OutputStream& out);
286
287     SpvId writeConstructorCompoundCast(const ConstructorCompoundCast& c, OutputStream& out);
288
289     SpvId writeComposite(const std::vector<SpvId>& arguments, const Type& type, OutputStream& out);
290
291     SpvId writeFieldAccess(const FieldAccess& f, OutputStream& out);
292
293     SpvId writeSwizzle(const Swizzle& swizzle, OutputStream& out);
294
295     /**
296      * Folds the potentially-vector result of a logical operation down to a single bool. If
297      * operandType is a vector type, assumes that the intermediate result in id is a bvec of the
298      * same dimensions, and applys all() to it to fold it down to a single bool value. Otherwise,
299      * returns the original id value.
300      */
301     SpvId foldToBool(SpvId id, const Type& operandType, SpvOp op, OutputStream& out);
302
303     SpvId writeMatrixComparison(const Type& operandType, SpvId lhs, SpvId rhs, SpvOp_ floatOperator,
304                                 SpvOp_ intOperator, SpvOp_ vectorMergeOperator,
305                                 SpvOp_ mergeOperator, OutputStream& out);
306
307     SpvId writeStructComparison(const Type& structType, SpvId lhs, Operator op, SpvId rhs,
308                                 OutputStream& out);
309
310     SpvId writeArrayComparison(const Type& structType, SpvId lhs, Operator op, SpvId rhs,
311                                OutputStream& out);
312
313     // Used by writeStructComparison and writeArrayComparison to logically combine field-by-field
314     // comparisons into an overall comparison result.
315     // - `a.x == b.x` merged with `a.y == b.y` generates `(a.x == b.x) && (a.y == b.y)`
316     // - `a.x != b.x` merged with `a.y != b.y` generates `(a.x != b.x) || (a.y != b.y)`
317     SpvId mergeComparisons(SpvId comparison, SpvId allComparisons, Operator op, OutputStream& out);
318
319     SpvId writeComponentwiseMatrixUnary(const Type& operandType,
320                                         SpvId operand,
321                                         SpvOp_ op,
322                                         OutputStream& out);
323
324     SpvId writeComponentwiseMatrixBinary(const Type& operandType, SpvId lhs, SpvId rhs,
325                                          SpvOp_ op, OutputStream& out);
326
327     SpvId writeBinaryOperation(const Type& resultType, const Type& operandType, SpvId lhs,
328                                SpvId rhs, SpvOp_ ifFloat, SpvOp_ ifInt, SpvOp_ ifUInt,
329                                SpvOp_ ifBool, OutputStream& out);
330
331     SpvId writeReciprocal(const Type& type, SpvId value, OutputStream& out);
332
333     SpvId writeBinaryExpression(const Type& leftType, SpvId lhs, Operator op,
334                                 const Type& rightType, SpvId rhs, const Type& resultType,
335                                 OutputStream& out);
336
337     SpvId writeBinaryExpression(const BinaryExpression& b, OutputStream& out);
338
339     SpvId writeTernaryExpression(const TernaryExpression& t, OutputStream& out);
340
341     SpvId writeIndexExpression(const IndexExpression& expr, OutputStream& out);
342
343     SpvId writeLogicalAnd(const Expression& left, const Expression& right, OutputStream& out);
344
345     SpvId writeLogicalOr(const Expression& left, const Expression& right, OutputStream& out);
346
347     SpvId writePrefixExpression(const PrefixExpression& p, OutputStream& out);
348
349     SpvId writePostfixExpression(const PostfixExpression& p, OutputStream& out);
350
351     SpvId writeLiteral(const Literal& f);
352
353     SpvId writeLiteral(double value, const Type& type);
354
355     void writeStatement(const Statement& s, OutputStream& out);
356
357     void writeBlock(const Block& b, OutputStream& out);
358
359     void writeIfStatement(const IfStatement& stmt, OutputStream& out);
360
361     void writeForStatement(const ForStatement& f, OutputStream& out);
362
363     void writeDoStatement(const DoStatement& d, OutputStream& out);
364
365     void writeSwitchStatement(const SwitchStatement& s, OutputStream& out);
366
367     void writeReturnStatement(const ReturnStatement& r, OutputStream& out);
368
369     void writeCapabilities(OutputStream& out);
370
371     void writeInstructions(const Program& program, OutputStream& out);
372
373     void writeOpCode(SpvOp_ opCode, int length, OutputStream& out);
374
375     void writeWord(int32_t word, OutputStream& out);
376
377     void writeString(std::string_view s, OutputStream& out);
378
379     void writeInstruction(SpvOp_ opCode, OutputStream& out);
380
381     void writeInstruction(SpvOp_ opCode, std::string_view string, OutputStream& out);
382
383     void writeInstruction(SpvOp_ opCode, int32_t word1, OutputStream& out);
384
385     void writeInstruction(SpvOp_ opCode, int32_t word1, std::string_view string,
386                           OutputStream& out);
387
388     void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, std::string_view string,
389                           OutputStream& out);
390
391     void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, OutputStream& out);
392
393     void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3,
394                           OutputStream& out);
395
396     void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4,
397                           OutputStream& out);
398
399     void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4,
400                           int32_t word5, OutputStream& out);
401
402     void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4,
403                           int32_t word5, int32_t word6, OutputStream& out);
404
405     void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4,
406                           int32_t word5, int32_t word6, int32_t word7, OutputStream& out);
407
408     void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4,
409                           int32_t word5, int32_t word6, int32_t word7, int32_t word8,
410                           OutputStream& out);
411
412     // This form of writeInstruction can deduplicate redundant ops.
413     struct Word;
414     // 8 Words is enough for nearly all instructions (except variable-length instructions like
415     // OpAccessChain or OpConstantComposite).
416     using Words = SkSTArray<8, Word>;
417     SpvId writeInstruction(SpvOp_ opCode, const SkTArray<Word>& words, OutputStream& out);
418
419     struct Instruction {
420         SpvId                  fOp;
421         int32_t                fResultKind;
422         SkSTArray<8, int32_t>  fWords;
423
424         bool operator==(const Instruction& that) const;
425         struct Hash;
426     };
427
428     static Instruction BuildInstructionKey(SpvOp_ opCode, const SkTArray<Word>& words);
429
430     // The writeOpXxxxx calls will simplify and deduplicate ops where possible.
431     SpvId writeOpConstantTrue(const Type& type);
432     SpvId writeOpConstantFalse(const Type& type);
433     SpvId writeOpConstant(const Type& type, int32_t valueBits);
434     SpvId writeOpConstantComposite(const Type& type, const SkTArray<SpvId>& values);
435     SpvId writeOpCompositeConstruct(const Type& type, const SkTArray<SpvId>&, OutputStream& out);
436     SpvId writeOpCompositeExtract(const Type& type, SpvId base, int component, OutputStream& out);
437     SpvId writeOpCompositeExtract(const Type& type, SpvId base, int componentA, int componentB,
438                                   OutputStream& out);
439     SpvId writeOpLoad(SpvId type, Precision precision, SpvId pointer, OutputStream& out);
440     void writeOpStore(SpvStorageClass_ storageClass, SpvId pointer, SpvId value, OutputStream& out);
441
442     // Converts the provided SpvId(s) into an array of scalar OpConstants, if it can be done.
443     bool toConstants(SpvId value, SkTArray<SpvId>* constants);
444     bool toConstants(SkSpan<const SpvId> values, SkTArray<SpvId>* constants);
445
446     // Extracts the requested component SpvId from a composite instruction, if it can be done.
447     Instruction* resultTypeForInstruction(const Instruction& instr);
448     int numComponentsForVecInstruction(const Instruction& instr);
449     SpvId toComponent(SpvId id, int component);
450
451     struct ConditionalOpCounts {
452         size_t numReachableOps;
453         size_t numStoreOps;
454     };
455     ConditionalOpCounts getConditionalOpCounts();
456     void pruneConditionalOps(ConditionalOpCounts ops);
457
458     enum StraightLineLabelType {
459         // Use "BranchlessBlock" for blocks which are never explicitly branched-to at all. This
460         // happens at the start of a function, or when we find unreachable code.
461         kBranchlessBlock,
462
463         // Use "BranchIsOnPreviousLine" when writing a label that comes immediately after its
464         // associated branch. Example usage:
465         // - SPIR-V does not implicitly fall through from one block to the next, so you may need to
466         //   use an OpBranch to explicitly jump to the next block, even when they are adjacent in
467         //   the code.
468         // - The block immediately following an OpBranchConditional or OpSwitch.
469         kBranchIsOnPreviousLine,
470     };
471
472     enum BranchingLabelType {
473         // Use "BranchIsAbove" for labels which are referenced by OpBranch or OpBranchConditional
474         // ops that are above the label in the code--i.e., the branch skips forward in the code.
475         kBranchIsAbove,
476
477         // Use "BranchIsBelow" for labels which are referenced by OpBranch or OpBranchConditional
478         // ops below the label in the code--i.e., the branch jumps backward in the code.
479         kBranchIsBelow,
480
481         // Use "BranchesOnBothSides" for labels which have branches coming from both directions.
482         kBranchesOnBothSides,
483     };
484     void writeLabel(SpvId label, StraightLineLabelType type, OutputStream& out);
485     void writeLabel(SpvId label, BranchingLabelType type, ConditionalOpCounts ops,
486                     OutputStream& out);
487
488     bool isDead(const Variable& var) const;
489
490     MemoryLayout memoryLayoutForVariable(const Variable&) const;
491
492     struct EntrypointAdapter {
493         std::unique_ptr<FunctionDefinition> entrypointDef;
494         std::unique_ptr<FunctionDeclaration> entrypointDecl;
495         Layout fLayout;
496         Modifiers fModifiers;
497     };
498
499     EntrypointAdapter writeEntrypointAdapter(const FunctionDeclaration& main);
500
501     struct UniformBuffer {
502         std::unique_ptr<InterfaceBlock> fInterfaceBlock;
503         std::unique_ptr<Variable> fInnerVariable;
504         std::unique_ptr<Type> fStruct;
505     };
506
507     void writeUniformBuffer(std::shared_ptr<SymbolTable> topLevelSymbolTable);
508
509     void addRTFlipUniform(Position pos);
510
511     const MemoryLayout fDefaultLayout;
512
513     uint64_t fCapabilities;
514     SpvId fIdCount;
515     SpvId fGLSLExtendedInstructions;
516     struct Intrinsic {
517         IntrinsicOpcodeKind opKind;
518         int32_t floatOp;
519         int32_t signedOp;
520         int32_t unsignedOp;
521         int32_t boolOp;
522     };
523     Intrinsic getIntrinsic(IntrinsicKind) const;
524     SkTHashMap<const FunctionDeclaration*, SpvId> fFunctionMap;
525     SkTHashMap<const Variable*, SpvId> fVariableMap;
526     SkTHashMap<const Type*, SpvId> fStructMap;
527     StringStream fGlobalInitializersBuffer;
528     StringStream fConstantBuffer;
529     StringStream fVariableBuffer;
530     StringStream fNameBuffer;
531     StringStream fDecorationBuffer;
532
533     // These caches map SpvIds to Instructions, and vice-versa. This enables us to deduplicate code
534     // (by detecting an Instruction we've already issued and reusing the SpvId), and to introspect
535     // and simplify code we've already emitted  (by taking a SpvId from an Instruction and following
536     // it back to its source).
537     SkTHashMap<Instruction, SpvId, Instruction::Hash> fOpCache;  // maps instruction -> SpvId
538     SkTHashMap<SpvId, Instruction> fSpvIdCache;                  // maps SpvId -> instruction
539     SkTHashMap<SpvId, SpvId> fStoreCache;                        // maps ptr SpvId -> value SpvId
540
541     // "Reachable" ops are instructions which can safely be accessed from the current block.
542     // For instance, if our SPIR-V contains `%3 = OpFAdd %1 %2`, we would be able to access and
543     // reuse that computation on following lines. However, if that Add operation occurred inside an
544     // `if` block, then its SpvId becomes inaccessible once we complete the if statement (since
545     // depending on the if condition, we may or may not have actually done that computation). The
546     // same logic applies to other control-flow blocks as well. Once an instruction becomes
547     // unreachable, we remove it from both op-caches.
548     std::vector<SpvId> fReachableOps;
549
550     // The "store-ops" list contains a running list of all the pointers in the store cache. If a
551     // store occurs inside of a conditional block, once that block exits, we no longer know what is
552     // stored in that particular SpvId. At that point, we must remove any associated entry from the
553     // store cache.
554     std::vector<SpvId> fStoreOps;
555
556     // label of the current block, or 0 if we are not in a block
557     SpvId fCurrentBlock;
558     std::stack<SpvId> fBreakTarget;
559     std::stack<SpvId> fContinueTarget;
560     bool fWroteRTFlip = false;
561     // holds variables synthesized during output, for lifetime purposes
562     SymbolTable fSynthetics;
563     // Holds a list of uniforms that were declared as globals at the top-level instead of in an
564     // interface block.
565     UniformBuffer fUniformBuffer;
566     std::vector<const VarDeclaration*> fTopLevelUniforms;
567     SkTHashMap<const Variable*, int> fTopLevelUniformMap; // <var, UniformBuffer field index>
568     SkTHashSet<const Variable*> fSPIRVBonusVariables;
569     SpvId fUniformBufferId = NA;
570
571     friend class PointerLValue;
572     friend class SwizzleLValue;
573
574     using INHERITED = CodeGenerator;
575 };
576
577 }  // namespace SkSL
578
579 #endif