• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2021 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // BuildSPIRV: Helper for OutputSPIRV to build SPIR-V.
7 //
8 
9 #ifndef COMPILER_TRANSLATOR_BUILDSPIRV_H_
10 #define COMPILER_TRANSLATOR_BUILDSPIRV_H_
11 
12 #include "common/FixedVector.h"
13 #include "common/PackedEnums.h"
14 #include "common/bitset_utils.h"
15 #include "common/hash_utils.h"
16 #include "common/spirv/spirv_instruction_builder_autogen.h"
17 #include "compiler/translator/Compiler.h"
18 
19 namespace spirv = angle::spirv;
20 
21 namespace sh
22 {
23 // Helper classes to map types to ids
24 
25 // The same GLSL type may map to multiple SPIR-V types when said GLSL type is used differently in
26 // the shader source, for example used with |invariant| and without, used in an interface block etc.
27 // This type contains the pieces of information that differentiate SPIR-V types derived from the
28 // same GLSL type.  This is referred to as "SPIR-V type specialization" henceforth.
29 struct SpirvType;
30 class SpirvTypeSpec
31 {
32   public:
33     // Some of the properties that specialize SPIR-V types apply to structs or arrays, but not to
34     // their fields or basic types.  When extracting fields, array elements, columns or basic types
35     // from a type, the following helpers are used to remove any ineffective (and thus incorrect)
36     // specialization.
37     void inferDefaults(const TType &type, TCompiler *compiler);
38     void onArrayElementSelection(bool isElementTypeBlock, bool isElementTypeArray);
39     void onBlockFieldSelection(const TType &fieldType);
40     void onMatrixColumnSelection();
41     void onVectorComponentSelection();
42 
43     // If a structure is used in two interface blocks with different layouts, it would have
44     // to generate two SPIR-V types, as its fields' Offset decorations could be different.
45     // For non-block types, when used in an interface block as an array, they could generate
46     // different ArrayStride decorations.  As such, the block storage is part of the SPIR-V type
47     // except for non-block non-array types.
48     TLayoutBlockStorage blockStorage = EbsUnspecified;
49 
50     // If a structure is used in two I/O blocks or output varyings with and without the invariant
51     // qualifier, it would also have to generate two SPIR-V types, as its fields' Invariant
52     // decorations would be different.
53     bool isInvariantBlock = false;
54 
55     // Similarly, a structure containing matrices may be used both with the column_major and
56     // row_major layout qualifier, generating two SPIR-V types with different decorations on its
57     // fields.
58     bool isRowMajorQualifiedBlock = false;
59 
60     // Arrays when used in an interface block produce a different type which is decorated with an
61     // ArrayStride.  Row-major qualified arrays of matrices can potentially produce a different
62     // stride from column-major ones.
63     bool isRowMajorQualifiedArray = false;
64 
65     // Bool is disallowed in interface blocks in SPIR-V.  This type is emulated with uint.  This
66     // property applies to both blocks with bools in them and the bool type inside the block itself.
67     bool isOrHasBoolInInterfaceBlock = false;
68 
69     // When |patch| is specified on an I/O block, the members of the type itself are decorated with
70     // it.  This is not recursively applied, and since each I/O block has a unique type, this
71     // doesn't actually result in duplicated types even if it's specializing the type.
72     bool isPatchIOBlock = false;
73 };
74 
75 struct SpirvType
76 {
77     // If struct or interface block, the type is identified by the pointer.  Note that both
78     // TStructure and TInterfaceBlock inherit from TFieldListCollection, and their difference is
79     // irrelevant as far as SPIR-V type is concerned.
80     const TFieldListCollection *block = nullptr;
81 
82     // Otherwise, it's a basic type + column, row and array dimensions, or it's an image
83     // declaration.
84     //
85     // Notes:
86     //
87     // - `precision` turns into a RelaxedPrecision decoration on the variable and instructions.
88     // - `precise` turns into a NoContraction decoration on the instructions.
89     // - `readonly`, `writeonly`, `coherent`, `volatile` and `restrict` only apply to memory object
90     //    declarations
91     // - `invariant` only applies to variable or members of a block
92     // - `matrixPacking` only applies to members of a struct
93     TBasicType type = EbtFloat;
94 
95     uint8_t primarySize   = 1;
96     uint8_t secondarySize = 1;
97 
98     TSpan<const unsigned int> arraySizes;
99 
100     // Only useful for image types.
101     TLayoutImageInternalFormat imageInternalFormat = EiifUnspecified;
102 
103     // For sampled images (i.e. GLSL samplers), there are two type ids; one is the OpTypeImage that
104     // declares the image itself, and one OpTypeSampledImage.  `isSamplerBaseImage` distinguishes
105     // between these two types.  Note that for the former, the basic type is still Ebt*Sampler* to
106     // distinguish it from storage images (which have a basic type of Ebt*Image*).
107     bool isSamplerBaseImage = false;
108 
109     // Anything that can cause the same GLSL type to produce different SPIR-V types.
110     SpirvTypeSpec typeSpec;
111 };
112 
113 struct SpirvIdAndIdList
114 {
115     spirv::IdRef id;
116     spirv::IdRefList idList;
117 
118     bool operator==(const SpirvIdAndIdList &other) const
119     {
120         return id == other.id && idList == other.idList;
121     }
122 };
123 
124 struct SpirvIdAndStorageClass
125 {
126     spirv::IdRef id;
127     spv::StorageClass storageClass;
128 
129     bool operator==(const SpirvIdAndStorageClass &other) const
130     {
131         return id == other.id && storageClass == other.storageClass;
132     }
133 };
134 
135 struct SpirvTypeHash
136 {
operatorSpirvTypeHash137     size_t operator()(const sh::SpirvType &type) const
138     {
139         // Block storage must only affect the type if it's a block type or array type (in a block).
140         ASSERT(type.typeSpec.blockStorage == sh::EbsUnspecified || type.block != nullptr ||
141                !type.arraySizes.empty());
142 
143         // Invariant must only affect the type if it's a block type.
144         ASSERT(!type.typeSpec.isInvariantBlock || type.block != nullptr);
145 
146         // Row-major block must only affect the type if it's a block type.
147         ASSERT(!type.typeSpec.isRowMajorQualifiedBlock || type.block != nullptr);
148 
149         // Patch must only affect the type if it's a block type.
150         ASSERT(!type.typeSpec.isPatchIOBlock || type.block != nullptr);
151 
152         // Row-major array must only affect the type if it's an array of non-square matrices in
153         // an std140 or std430 block.
154         ASSERT(!type.typeSpec.isRowMajorQualifiedArray ||
155                (type.block == nullptr && !type.arraySizes.empty() && type.secondarySize > 1 &&
156                 type.primarySize != type.secondarySize &&
157                 type.typeSpec.blockStorage != sh::EbsUnspecified));
158 
159         size_t result = 0;
160 
161         if (!type.arraySizes.empty())
162         {
163             result = angle::ComputeGenericHash(type.arraySizes.data(),
164                                                type.arraySizes.size() * sizeof(type.arraySizes[0]));
165         }
166 
167         if (type.block != nullptr)
168         {
169             return result ^ angle::ComputeGenericHash(&type.block, sizeof(type.block)) ^
170                    static_cast<size_t>(type.typeSpec.isInvariantBlock) ^
171                    (static_cast<size_t>(type.typeSpec.isRowMajorQualifiedBlock) << 1) ^
172                    (static_cast<size_t>(type.typeSpec.isRowMajorQualifiedArray) << 2) ^
173                    (static_cast<size_t>(type.typeSpec.isPatchIOBlock) << 3) ^
174                    (type.typeSpec.blockStorage << 4);
175         }
176 
177         static_assert(sh::EbtLast < 256, "Basic type doesn't fit in uint8_t");
178         static_assert(sh::EbsLast < 8, "Block storage doesn't fit in 3 bits");
179         static_assert(sh::EiifLast < 32, "Image format doesn't fit in 5 bits");
180         ASSERT(type.primarySize > 0 && type.primarySize <= 4);
181         ASSERT(type.secondarySize > 0 && type.secondarySize <= 4);
182 
183         const uint8_t properties[4] = {
184             static_cast<uint8_t>(type.type),
185             static_cast<uint8_t>((type.primarySize - 1) | (type.secondarySize - 1) << 2 |
186                                  type.isSamplerBaseImage << 4),
187             static_cast<uint8_t>(type.typeSpec.blockStorage | type.imageInternalFormat << 3),
188             // Padding because ComputeGenericHash expects a key size divisible by 4
189         };
190 
191         return result ^ angle::ComputeGenericHash(properties, sizeof(properties));
192     }
193 };
194 
195 struct SpirvIdAndIdListHash
196 {
operatorSpirvIdAndIdListHash197     size_t operator()(const SpirvIdAndIdList &key) const
198     {
199         return angle::ComputeGenericHash(key.idList.data(),
200                                          key.idList.size() * sizeof(key.idList[0])) ^
201                key.id;
202     }
203 };
204 
205 struct SpirvIdAndStorageClassHash
206 {
operatorSpirvIdAndStorageClassHash207     size_t operator()(const SpirvIdAndStorageClass &key) const
208     {
209         ASSERT(key.storageClass < 16);
210         return key.storageClass | key.id << 4;
211     }
212 };
213 
214 // Data tracked per SPIR-V type (keyed by SpirvType).
215 struct SpirvTypeData
216 {
217     // The SPIR-V id corresponding to the type.
218     spirv::IdRef id;
219 };
220 
221 // Decorations to be applied to variable or intermediate ids which are not part of the SPIR-V type
222 // and are not specific enough (like DescriptorSet) to be handled automatically.  Currently, these
223 // are:
224 //
225 //     RelaxedPrecision: used to implement |lowp| and |mediump|
226 //     NoContraction: used to implement |precise|.  TODO: support this.  It requires the precise
227 //                    property to be promoted through the nodes in the AST, which currently isn't.
228 //                    http://anglebug.com/4889
229 //     Invariant: used to implement |invariant|, which is applied to output variables.
230 //
231 // Note that Invariant applies to variables and NoContraction to arithmetic instructions, so they
232 // are mutually exclusive and a maximum of 2 decorations are possible.  FixedVector::push_back will
233 // ASSERT if the given size is ever not enough.
234 using SpirvDecorations = angle::FixedVector<spv::Decoration, 2>;
235 
236 // A block of code.  SPIR-V produces forward references to blocks, such as OpBranchConditional
237 // specifying the id of the if and else blocks, each of those referencing the id of the block after
238 // the else.  Additionally, local variable declarations are accumulated at the top of the first
239 // block in a function.  For these reasons, each block of SPIR-V is generated separately and
240 // assembled at the end of the function, allowing prior blocks to be modified when necessary.
241 struct SpirvBlock
242 {
243     // Id of the block
244     spirv::IdRef labelId;
245 
246     // Local variable declarations.  Only the first block of a function is allowed to contain any
247     // instructions here.
248     spirv::Blob localVariables;
249 
250     // Everything *after* OpLabel (which itself is not generated until blocks are assembled) and
251     // local variables.
252     spirv::Blob body;
253 
254     // Whether the block is terminated.  Useful for functions without return, asserting that code is
255     // not added after return/break/continue etc (i.e. dead code, which should really be removed
256     // earlier by a transformation, but could also be hacked by returning a bogus block to contain
257     // all the "garbage" to throw away), last switch case without a break, etc.
258     bool isTerminated = false;
259 };
260 
261 // Conditional code, constituting ifs, switches and loops.
262 struct SpirvConditional
263 {
264     // The id of blocks that make up the conditional.
265     //
266     // - For if, there are three blocks: the then, else and merge blocks
267     // - For loops, there are four blocks: the condition, body, continue and merge blocks
268     // - For switch, there are a number of blocks based on the cases.
269     //
270     // In all cases, the merge block is the last block in this list.  When the conditional is done
271     // with, that's the block that will be made "current" and future instructions written to.  The
272     // merge block is also the branch target of "break" instructions.
273     //
274     // For loops, the continue target block is the one before last block in this list.
275     std::vector<spirv::IdRef> blockIds;
276 
277     // Up to which block is already generated.  Used by nextConditionalBlock() to generate a block
278     // and give it an id pre-determined in blockIds.
279     size_t nextBlockToWrite = 0;
280 
281     // Used to determine if continue will affect this (i.e. it's a loop).
282     bool isContinuable = false;
283     // Used to determine if break will affect this (i.e. it's a loop or switch).
284     bool isBreakable = false;
285 };
286 
287 // List of known extensions
288 enum class SPIRVExtensions
289 {
290     // GL_OVR_multiview / SPV_KHR_multiview
291     MultiviewOVR = 0,
292 
293     InvalidEnum = 1,
294     EnumCount   = 1,
295 };
296 
297 // Helper class to construct SPIR-V
298 class SPIRVBuilder : angle::NonCopyable
299 {
300   public:
301     SPIRVBuilder(TCompiler *compiler,
302                  ShCompileOptions compileOptions,
303                  ShHashFunction64 hashFunction,
304                  NameMap &nameMap);
305 
306     spirv::IdRef getNewId(const SpirvDecorations &decorations);
307     SpirvType getSpirvType(const TType &type, const SpirvTypeSpec &typeSpec) const;
308     const SpirvTypeData &getTypeData(const TType &type, const SpirvTypeSpec &typeSpec);
309     const SpirvTypeData &getTypeDataOverrideTypeSpec(const TType &type,
310                                                      const SpirvTypeSpec &typeSpec);
311     const SpirvTypeData &getSpirvTypeData(const SpirvType &type, const TSymbol *block);
312     spirv::IdRef getBasicTypeId(TBasicType basicType, size_t size);
313     spirv::IdRef getTypePointerId(spirv::IdRef typeId, spv::StorageClass storageClass);
314     spirv::IdRef getFunctionTypeId(spirv::IdRef returnTypeId, const spirv::IdRefList &paramTypeIds);
315 
316     // Decorations that may apply to intermediate instructions (in addition to variables).
317     // |precise| is only applicable to arithmetic nodes.
318     SpirvDecorations getDecorations(const TType &type);
319     SpirvDecorations getArithmeticDecorations(const TType &type, bool isPrecise, TOperator op);
320 
321     // Extended instructions
322     spirv::IdRef getExtInstImportIdStd();
323 
getSpirvDebug()324     spirv::Blob *getSpirvDebug() { return &mSpirvDebug; }
getSpirvDecorations()325     spirv::Blob *getSpirvDecorations() { return &mSpirvDecorations; }
getSpirvTypeAndConstantDecls()326     spirv::Blob *getSpirvTypeAndConstantDecls() { return &mSpirvTypeAndConstantDecls; }
getSpirvTypePointerDecls()327     spirv::Blob *getSpirvTypePointerDecls() { return &mSpirvTypePointerDecls; }
getSpirvFunctionTypeDecls()328     spirv::Blob *getSpirvFunctionTypeDecls() { return &mSpirvFunctionTypeDecls; }
getSpirvVariableDecls()329     spirv::Blob *getSpirvVariableDecls() { return &mSpirvVariableDecls; }
getSpirvFunctions()330     spirv::Blob *getSpirvFunctions() { return &mSpirvFunctions; }
getSpirvCurrentFunctionBlock()331     spirv::Blob *getSpirvCurrentFunctionBlock()
332     {
333         ASSERT(!mSpirvCurrentFunctionBlocks.empty() &&
334                !mSpirvCurrentFunctionBlocks.back().isTerminated);
335         return &mSpirvCurrentFunctionBlocks.back().body;
336     }
getSpirvCurrentFunctionBlockId()337     spirv::IdRef getSpirvCurrentFunctionBlockId()
338     {
339         ASSERT(!mSpirvCurrentFunctionBlocks.empty() &&
340                !mSpirvCurrentFunctionBlocks.back().isTerminated);
341         return mSpirvCurrentFunctionBlocks.back().labelId;
342     }
isCurrentFunctionBlockTerminated()343     bool isCurrentFunctionBlockTerminated() const
344     {
345         ASSERT(!mSpirvCurrentFunctionBlocks.empty());
346         return mSpirvCurrentFunctionBlocks.back().isTerminated;
347     }
terminateCurrentFunctionBlock()348     void terminateCurrentFunctionBlock()
349     {
350         ASSERT(!mSpirvCurrentFunctionBlocks.empty());
351         mSpirvCurrentFunctionBlocks.back().isTerminated = true;
352     }
getCurrentConditional()353     const SpirvConditional *getCurrentConditional() { return &mConditionalStack.back(); }
354 
355     bool isInvariantOutput(const TType &type) const;
356 
357     void addCapability(spv::Capability capability);
358     void addExecutionMode(spv::ExecutionMode executionMode);
359     void addExtension(SPIRVExtensions extension);
360     void setEntryPointId(spirv::IdRef id);
361     void addEntryPointInterfaceVariableId(spirv::IdRef id);
362     void writePerVertexBuiltIns(const TType &type, spirv::IdRef typeId);
363     void writeInterfaceVariableDecorations(const TType &type, spirv::IdRef variableId);
364     void writeBranchConditional(spirv::IdRef conditionValue,
365                                 spirv::IdRef trueBlock,
366                                 spirv::IdRef falseBlock,
367                                 spirv::IdRef mergeBlock);
368     void writeBranchConditionalBlockEnd();
369     void writeLoopHeader(spirv::IdRef branchToBlock,
370                          spirv::IdRef continueBlock,
371                          spirv::IdRef mergeBlock);
372     void writeLoopConditionEnd(spirv::IdRef conditionValue,
373                                spirv::IdRef branchToBlock,
374                                spirv::IdRef mergeBlock);
375     void writeLoopContinueEnd(spirv::IdRef headerBlock);
376     void writeLoopBodyEnd(spirv::IdRef continueBlock);
377     void writeSwitch(spirv::IdRef conditionValue,
378                      spirv::IdRef defaultBlock,
379                      const spirv::PairLiteralIntegerIdRefList &targetPairList,
380                      spirv::IdRef mergeBlock);
381     void writeSwitchCaseBlockEnd();
382 
383     spirv::IdRef getBoolConstant(bool value);
384     spirv::IdRef getUintConstant(uint32_t value);
385     spirv::IdRef getIntConstant(int32_t value);
386     spirv::IdRef getFloatConstant(float value);
387     spirv::IdRef getUvecConstant(uint32_t value, int size);
388     spirv::IdRef getIvecConstant(int32_t value, int size);
389     spirv::IdRef getVecConstant(float value, int size);
390     spirv::IdRef getCompositeConstant(spirv::IdRef typeId, const spirv::IdRefList &values);
391     spirv::IdRef getNullConstant(spirv::IdRef typeId);
392 
393     // Helpers to start and end a function.
394     void startNewFunction(spirv::IdRef functionId, const TFunction *func);
395     void assembleSpirvFunctionBlocks();
396 
397     // Helper to declare a variable.  Function-local variables must be placed in the first block of
398     // the current function.
399     spirv::IdRef declareVariable(spirv::IdRef typeId,
400                                  spv::StorageClass storageClass,
401                                  const SpirvDecorations &decorations,
402                                  spirv::IdRef *initializerId,
403                                  const char *name);
404     // Helper to declare specialization constants.
405     spirv::IdRef declareSpecConst(TBasicType type, int id, const char *name);
406 
407     // Helpers for conditionals.
408     void startConditional(size_t blockCount, bool isContinuable, bool isBreakable);
409     void nextConditionalBlock();
410     void endConditional();
411     bool isInLoop() const;
412     spirv::IdRef getBreakTargetId() const;
413     spirv::IdRef getContinueTargetId() const;
414 
415     // TODO: remove name hashing once translation through glslang is removed.  That is necessary to
416     // avoid name collision between ANGLE's internal symbols and user-defined ones when compiling
417     // the generated GLSL, but is irrelevant when generating SPIR-V directly.  Currently, the SPIR-V
418     // transformer relies on the "mapped" names, which should also be changed when this hashing is
419     // removed.
420     ImmutableString hashName(const TSymbol *symbol);
421     ImmutableString hashTypeName(const TType &type);
422     ImmutableString hashFieldName(const TField *field);
423     ImmutableString hashFunctionName(const TFunction *func);
424 
425     spirv::Blob getSpirv();
426 
427   private:
428     SpirvTypeData declareType(const SpirvType &type, const TSymbol *block);
429 
430     uint32_t calculateBaseAlignmentAndSize(const SpirvType &type, uint32_t *sizeInStorageBlockOut);
431     uint32_t calculateSizeAndWriteOffsetDecorations(const SpirvType &type,
432                                                     spirv::IdRef typeId,
433                                                     uint32_t blockBaseAlignment);
434     void writeMemberDecorations(const SpirvType &type, spirv::IdRef typeId);
435     void writeInterpolationDecoration(TQualifier qualifier, spirv::IdRef id, uint32_t fieldIndex);
436 
437     // Helpers for type declaration.
438     void getImageTypeParameters(TBasicType type,
439                                 spirv::IdRef *sampledTypeOut,
440                                 spv::Dim *dimOut,
441                                 spirv::LiteralInteger *depthOut,
442                                 spirv::LiteralInteger *arrayedOut,
443                                 spirv::LiteralInteger *multisampledOut,
444                                 spirv::LiteralInteger *sampledOut);
445     spv::ImageFormat getImageFormat(TLayoutImageInternalFormat imageInternalFormat);
446 
447     spirv::IdRef getBasicConstantHelper(uint32_t value,
448                                         TBasicType type,
449                                         angle::HashMap<uint32_t, spirv::IdRef> *constants);
450     spirv::IdRef getNullVectorConstantHelper(TBasicType type, int size);
451     spirv::IdRef getVectorConstantHelper(spirv::IdRef valueId, TBasicType type, int size);
452 
453     uint32_t nextUnusedBinding();
454     uint32_t nextUnusedInputLocation(uint32_t consumedCount);
455     uint32_t nextUnusedOutputLocation(uint32_t consumedCount);
456 
457     void writeExecutionModes(spirv::Blob *blob);
458     void writeExtensions(spirv::Blob *blob);
459     void writeSourceExtensions(spirv::Blob *blob);
460 
461     ANGLE_MAYBE_UNUSED TCompiler *mCompiler;
462     ShCompileOptions mCompileOptions;
463     gl::ShaderType mShaderType;
464 
465     // Capabilities the shader is using.  Accumulated as the instructions are generated.  The Shader
466     // capability is unconditionally generated, so it's not tracked.
467     std::set<spv::Capability> mCapabilities;
468     // Execution modes the shader is using.  Most execution modes are automatically derived from
469     // shader metadata, but some are only discovered while traversing the tree.  Only the latter
470     // execution modes are stored here.
471     angle::BitSet<32> mExecutionModes;
472     // Extensions used by the shader.
473     angle::PackedEnumBitSet<SPIRVExtensions> mExtensions;
474 
475     // The list of interface variables and the id of main() populated as the instructions are
476     // generated.  Used for the OpEntryPoint instruction.
477     spirv::IdRefList mEntryPointInterfaceList;
478     spirv::IdRef mEntryPointId;
479 
480     // Id of imported instructions, if used.
481     spirv::IdRef mExtInstImportIdStd;
482 
483     // Current ID bound, used to allocate new ids.
484     spirv::IdRef mNextAvailableId;
485 
486     // A map from the AST type to the corresponding SPIR-V ID and associated data.  Note that TType
487     // includes a lot of information that pertains to the variable that has the type, not the type
488     // itself.  SpirvType instead contains only information that can identify the type itself.
489     angle::HashMap<SpirvType, SpirvTypeData, SpirvTypeHash> mTypeMap;
490 
491     // Various sections of SPIR-V.  Each section grows as SPIR-V is generated, and the final result
492     // is obtained by stitching the sections together.  This puts the instructions in the order
493     // required by the spec.
494     spirv::Blob mSpirvDebug;
495     spirv::Blob mSpirvDecorations;
496     spirv::Blob mSpirvTypeAndConstantDecls;
497     spirv::Blob mSpirvTypePointerDecls;
498     spirv::Blob mSpirvFunctionTypeDecls;
499     spirv::Blob mSpirvVariableDecls;
500     spirv::Blob mSpirvFunctions;
501     // A list of blocks created for the current function.  These are assembled by
502     // assembleSpirvFunctionBlocks() when the function is entirely visited.  Local variables need to
503     // be inserted at the beginning of the first function block, so the entire SPIR-V of the
504     // function cannot be obtained until it's fully visited.
505     //
506     // The last block in this list is the one currently being written to.
507     std::vector<SpirvBlock> mSpirvCurrentFunctionBlocks;
508 
509     // List of constants that are already defined (for reuse).
510     spirv::IdRef mBoolConstants[2];
511     angle::HashMap<uint32_t, spirv::IdRef> mUintConstants;
512     angle::HashMap<uint32_t, spirv::IdRef> mIntConstants;
513     angle::HashMap<uint32_t, spirv::IdRef> mFloatConstants;
514     angle::HashMap<SpirvIdAndIdList, spirv::IdRef, SpirvIdAndIdListHash> mCompositeConstants;
515     // Keyed by typeId, returns the null constant corresponding to that type.
516     std::vector<spirv::IdRef> mNullConstants;
517 
518     // List of type pointers that are already defined.
519     // TODO: if all users call getTypeData(), move to SpirvTypeData.  http://anglebug.com/4889
520     angle::HashMap<SpirvIdAndStorageClass, spirv::IdRef, SpirvIdAndStorageClassHash>
521         mTypePointerIdMap;
522 
523     // List of function types that are already defined.
524     angle::HashMap<SpirvIdAndIdList, spirv::IdRef, SpirvIdAndIdListHash> mFunctionTypeIdMap;
525 
526     // Stack of conditionals.  When an if, loop or switch is visited, a new conditional scope is
527     // added.  When the conditional construct is entirely visited, it's popped.  As the blocks of
528     // the conditional constructs are visited, ids are consumed from the top of the stack.  When
529     // break or continue is visited, the stack is traversed backwards until a loop or switch is
530     // found.
531     std::vector<SpirvConditional> mConditionalStack;
532 
533     // name hashing.
534     ShHashFunction64 mHashFunction;
535     NameMap &mNameMap;
536 
537     // Every resource that requires set & binding layout qualifiers is assigned set 0 and an
538     // arbitrary binding.  Every input/output that requires a location layout qualifier is assigned
539     // an arbitrary location as well.
540     //
541     // The link-time SPIR-V transformer modifies set, binding and location decorations in SPIR-V
542     // directly.
543     uint32_t mNextUnusedBinding;
544     uint32_t mNextUnusedInputLocation;
545     uint32_t mNextUnusedOutputLocation;
546 };
547 }  // namespace sh
548 
549 #endif  // COMPILER_TRANSLATOR_BUILDSPIRV_H_
550