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