• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (C) 2014-2015 LunarG, Inc.
3 // Copyright (C) 2015-2020 Google, Inc.
4 // Copyright (C) 2017 ARM Limited.
5 // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.
6 //
7 // All rights reserved.
8 //
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions
11 // are met:
12 //
13 //    Redistributions of source code must retain the above copyright
14 //    notice, this list of conditions and the following disclaimer.
15 //
16 //    Redistributions in binary form must reproduce the above
17 //    copyright notice, this list of conditions and the following
18 //    disclaimer in the documentation and/or other materials provided
19 //    with the distribution.
20 //
21 //    Neither the name of 3Dlabs Inc. Ltd. nor the names of its
22 //    contributors may be used to endorse or promote products derived
23 //    from this software without specific prior written permission.
24 //
25 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 // POSSIBILITY OF SUCH DAMAGE.
37 
38 //
39 // "Builder" is an interface to fully build SPIR-V IR.   Allocate one of
40 // these to build (a thread safe) internal SPIR-V representation (IR),
41 // and then dump it as a binary stream according to the SPIR-V specification.
42 //
43 // A Builder has a 1:1 relationship with a SPIR-V module.
44 //
45 
46 #pragma once
47 #ifndef SpvBuilder_H
48 #define SpvBuilder_H
49 
50 #include "Logger.h"
51 #include "spirv.hpp"
52 #include "spvIR.h"
53 namespace spv {
54     #include "GLSL.ext.KHR.h"
55     #include "NonSemanticShaderDebugInfo100.h"
56 }
57 
58 #include <algorithm>
59 #include <map>
60 #include <memory>
61 #include <set>
62 #include <sstream>
63 #include <stack>
64 #include <unordered_map>
65 #include <map>
66 
67 namespace spv {
68 
69 typedef enum {
70     Spv_1_0 = (1 << 16),
71     Spv_1_1 = (1 << 16) | (1 << 8),
72     Spv_1_2 = (1 << 16) | (2 << 8),
73     Spv_1_3 = (1 << 16) | (3 << 8),
74     Spv_1_4 = (1 << 16) | (4 << 8),
75     Spv_1_5 = (1 << 16) | (5 << 8),
76 } SpvVersion;
77 
78 class Builder {
79 public:
80     Builder(unsigned int spvVersion, unsigned int userNumber, SpvBuildLogger* logger);
81     virtual ~Builder();
82 
83     static const int maxMatrixSize = 4;
84 
getSpvVersion()85     unsigned int getSpvVersion() const { return spvVersion; }
86 
setSource(spv::SourceLanguage lang,int version)87     void setSource(spv::SourceLanguage lang, int version)
88     {
89         sourceLang = lang;
90         sourceVersion = version;
91     }
getStringId(const std::string & str)92     spv::Id getStringId(const std::string& str)
93     {
94         auto sItr = stringIds.find(str);
95         if (sItr != stringIds.end())
96             return sItr->second;
97         spv::Id strId = getUniqueId();
98         Instruction* fileString = new Instruction(strId, NoType, OpString);
99         const char* file_c_str = str.c_str();
100         fileString->addStringOperand(file_c_str);
101         strings.push_back(std::unique_ptr<Instruction>(fileString));
102         module.mapInstruction(fileString);
103         stringIds[file_c_str] = strId;
104         return strId;
105     }
getSourceFile()106     spv::Id getSourceFile() const
107     {
108         return sourceFileStringId;
109     }
setSourceFile(const std::string & file)110     void setSourceFile(const std::string& file)
111     {
112         sourceFileStringId = getStringId(file);
113         currentFileId = sourceFileStringId;
114     }
setSourceText(const std::string & text)115     void setSourceText(const std::string& text) { sourceText = text; }
addSourceExtension(const char * ext)116     void addSourceExtension(const char* ext) { sourceExtensions.push_back(ext); }
addModuleProcessed(const std::string & p)117     void addModuleProcessed(const std::string& p) { moduleProcesses.push_back(p.c_str()); }
setEmitOpLines()118     void setEmitOpLines() { emitOpLines = true; }
setEmitNonSemanticShaderDebugInfo(bool const emit)119     void setEmitNonSemanticShaderDebugInfo(bool const emit)
120     {
121         emitNonSemanticShaderDebugInfo = emit;
122 
123         if(emit)
124         {
125             importNonSemanticShaderDebugInfoInstructions();
126         }
127     }
setEmitNonSemanticShaderDebugSource(bool const src)128     void setEmitNonSemanticShaderDebugSource(bool const src)
129     {
130         emitNonSemanticShaderDebugSource = src;
131     }
addExtension(const char * ext)132     void addExtension(const char* ext) { extensions.insert(ext); }
removeExtension(const char * ext)133     void removeExtension(const char* ext)
134     {
135         extensions.erase(ext);
136     }
addIncorporatedExtension(const char * ext,SpvVersion incorporatedVersion)137     void addIncorporatedExtension(const char* ext, SpvVersion incorporatedVersion)
138     {
139         if (getSpvVersion() < static_cast<unsigned>(incorporatedVersion))
140             addExtension(ext);
141     }
promoteIncorporatedExtension(const char * baseExt,const char * promoExt,SpvVersion incorporatedVersion)142     void promoteIncorporatedExtension(const char* baseExt, const char* promoExt, SpvVersion incorporatedVersion)
143     {
144         removeExtension(baseExt);
145         addIncorporatedExtension(promoExt, incorporatedVersion);
146     }
addInclude(const std::string & name,const std::string & text)147     void addInclude(const std::string& name, const std::string& text)
148     {
149         spv::Id incId = getStringId(name);
150         includeFiles[incId] = &text;
151     }
152     Id import(const char*);
setMemoryModel(spv::AddressingModel addr,spv::MemoryModel mem)153     void setMemoryModel(spv::AddressingModel addr, spv::MemoryModel mem)
154     {
155         addressModel = addr;
156         memoryModel = mem;
157     }
158 
addCapability(spv::Capability cap)159     void addCapability(spv::Capability cap) { capabilities.insert(cap); }
160 
161     // To get a new <id> for anything needing a new one.
getUniqueId()162     Id getUniqueId() { return ++uniqueId; }
163 
164     // To get a set of new <id>s, e.g., for a set of function parameters
getUniqueIds(int numIds)165     Id getUniqueIds(int numIds)
166     {
167         Id id = uniqueId + 1;
168         uniqueId += numIds;
169         return id;
170     }
171 
172     // Generate OpLine for non-filename-based #line directives (ie no filename
173     // seen yet): Log the current line, and if different than the last one,
174     // issue a new OpLine using the new line and current source file name.
175     void setLine(int line);
176 
177     // If filename null, generate OpLine for non-filename-based line directives,
178     // else do filename-based: Log the current line and file, and if different
179     // than the last one, issue a new OpLine using the new line and file
180     // name.
181     void setLine(int line, const char* filename);
182     // Low-level OpLine. See setLine() for a layered helper.
183     void addLine(Id fileName, int line, int column);
184     void addDebugScopeAndLine(Id fileName, int line, int column);
185 
186     // For creating new types (will return old type if the requested one was already made).
187     Id makeVoidType();
188     Id makeBoolType(bool const compilerGenerated = true);
189     Id makePointer(StorageClass, Id pointee);
190     Id makeForwardPointer(StorageClass);
191     Id makePointerFromForwardPointer(StorageClass, Id forwardPointerType, Id pointee);
192     Id makeIntegerType(int width, bool hasSign);   // generic
makeIntType(int width)193     Id makeIntType(int width) { return makeIntegerType(width, true); }
makeUintType(int width)194     Id makeUintType(int width) { return makeIntegerType(width, false); }
195     Id makeFloatType(int width);
196     Id makeStructType(const std::vector<Id>& members, const char* name, bool const compilerGenerated = true);
197     Id makeStructResultType(Id type0, Id type1);
198     Id makeVectorType(Id component, int size);
199     Id makeMatrixType(Id component, int cols, int rows);
200     Id makeArrayType(Id element, Id sizeId, int stride);  // 0 stride means no stride decoration
201     Id makeRuntimeArray(Id element);
202     Id makeFunctionType(Id returnType, const std::vector<Id>& paramTypes);
203     Id makeImageType(Id sampledType, Dim, bool depth, bool arrayed, bool ms, unsigned sampled, ImageFormat format);
204     Id makeSamplerType();
205     Id makeSampledImageType(Id imageType);
206     Id makeCooperativeMatrixType(Id component, Id scope, Id rows, Id cols);
207     Id makeGenericType(spv::Op opcode, std::vector<spv::IdImmediate>& operands);
208 
209     // SPIR-V NonSemantic Shader DebugInfo Instructions
210     struct DebugTypeLoc {
211         std::string name {};
212         int line {0};
213         int column {0};
214     };
215     std::unordered_map<Id, DebugTypeLoc> debugTypeLocs;
216     Id makeDebugInfoNone();
217     Id makeBoolDebugType(int const size);
218     Id makeIntegerDebugType(int const width, bool const hasSign);
219     Id makeFloatDebugType(int const width);
220     Id makeSequentialDebugType(Id const baseType, Id const componentCount, NonSemanticShaderDebugInfo100Instructions const sequenceType);
221     Id makeArrayDebugType(Id const baseType, Id const componentCount);
222     Id makeVectorDebugType(Id const baseType, int const componentCount);
223     Id makeMatrixDebugType(Id const vectorType, int const vectorCount, bool columnMajor = true);
224     Id makeMemberDebugType(Id const memberType, DebugTypeLoc const& debugTypeLoc);
225     Id makeCompositeDebugType(std::vector<Id> const& memberTypes, char const*const name,
226         NonSemanticShaderDebugInfo100DebugCompositeType const tag, bool const isOpaqueType = false);
227     Id makeDebugSource(const Id fileName);
228     Id makeDebugCompilationUnit();
229     Id createDebugGlobalVariable(Id const type, char const*const name, Id const variable);
230     Id createDebugLocalVariable(Id type, char const*const name, size_t const argNumber = 0);
231     Id makeDebugExpression();
232     Id makeDebugDeclare(Id const debugLocalVariable, Id const localVariable);
233     Id makeDebugValue(Id const debugLocalVariable, Id const value);
234     Id makeDebugFunctionType(Id returnType, const std::vector<Id>& paramTypes);
235     Id makeDebugFunction(Function* function, Id nameId, Id funcTypeId);
236     Id makeDebugLexicalBlock(uint32_t line);
237     std::string unmangleFunctionName(std::string const& name) const;
238 
239     // accelerationStructureNV type
240     Id makeAccelerationStructureType();
241     // rayQueryEXT type
242     Id makeRayQueryType();
243 
244     // For querying about types.
getTypeId(Id resultId)245     Id getTypeId(Id resultId) const { return module.getTypeId(resultId); }
246     Id getDerefTypeId(Id resultId) const;
getOpCode(Id id)247     Op getOpCode(Id id) const { return module.getInstruction(id)->getOpCode(); }
getTypeClass(Id typeId)248     Op getTypeClass(Id typeId) const { return getOpCode(typeId); }
249     Op getMostBasicTypeClass(Id typeId) const;
getNumComponents(Id resultId)250     int getNumComponents(Id resultId) const { return getNumTypeComponents(getTypeId(resultId)); }
251     int getNumTypeConstituents(Id typeId) const;
getNumTypeComponents(Id typeId)252     int getNumTypeComponents(Id typeId) const { return getNumTypeConstituents(typeId); }
253     Id getScalarTypeId(Id typeId) const;
254     Id getContainedTypeId(Id typeId) const;
255     Id getContainedTypeId(Id typeId, int) const;
getTypeStorageClass(Id typeId)256     StorageClass getTypeStorageClass(Id typeId) const { return module.getStorageClass(typeId); }
getImageTypeFormat(Id typeId)257     ImageFormat getImageTypeFormat(Id typeId) const
258         { return (ImageFormat)module.getInstruction(typeId)->getImmediateOperand(6); }
259     Id getResultingAccessChainType() const;
260 
isPointer(Id resultId)261     bool isPointer(Id resultId)      const { return isPointerType(getTypeId(resultId)); }
isScalar(Id resultId)262     bool isScalar(Id resultId)       const { return isScalarType(getTypeId(resultId)); }
isVector(Id resultId)263     bool isVector(Id resultId)       const { return isVectorType(getTypeId(resultId)); }
isMatrix(Id resultId)264     bool isMatrix(Id resultId)       const { return isMatrixType(getTypeId(resultId)); }
isCooperativeMatrix(Id resultId)265     bool isCooperativeMatrix(Id resultId)const { return isCooperativeMatrixType(getTypeId(resultId)); }
isAggregate(Id resultId)266     bool isAggregate(Id resultId)    const { return isAggregateType(getTypeId(resultId)); }
isSampledImage(Id resultId)267     bool isSampledImage(Id resultId) const { return isSampledImageType(getTypeId(resultId)); }
268 
isBoolType(Id typeId)269     bool isBoolType(Id typeId)
270         { return groupedTypes[OpTypeBool].size() > 0 && typeId == groupedTypes[OpTypeBool].back()->getResultId(); }
isIntType(Id typeId)271     bool isIntType(Id typeId)          const
272         { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) != 0; }
isUintType(Id typeId)273     bool isUintType(Id typeId)         const
274         { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) == 0; }
isFloatType(Id typeId)275     bool isFloatType(Id typeId)        const { return getTypeClass(typeId) == OpTypeFloat; }
isPointerType(Id typeId)276     bool isPointerType(Id typeId)      const { return getTypeClass(typeId) == OpTypePointer; }
isScalarType(Id typeId)277     bool isScalarType(Id typeId)       const
278         { return getTypeClass(typeId) == OpTypeFloat || getTypeClass(typeId) == OpTypeInt ||
279           getTypeClass(typeId) == OpTypeBool; }
isVectorType(Id typeId)280     bool isVectorType(Id typeId)       const { return getTypeClass(typeId) == OpTypeVector; }
isMatrixType(Id typeId)281     bool isMatrixType(Id typeId)       const { return getTypeClass(typeId) == OpTypeMatrix; }
isStructType(Id typeId)282     bool isStructType(Id typeId)       const { return getTypeClass(typeId) == OpTypeStruct; }
isArrayType(Id typeId)283     bool isArrayType(Id typeId)        const { return getTypeClass(typeId) == OpTypeArray; }
284 #ifdef GLSLANG_WEB
isCooperativeMatrixType(Id typeId)285     bool isCooperativeMatrixType(Id typeId)const { return false; }
286 #else
isCooperativeMatrixType(Id typeId)287     bool isCooperativeMatrixType(Id typeId)const { return getTypeClass(typeId) == OpTypeCooperativeMatrixNV; }
288 #endif
isAggregateType(Id typeId)289     bool isAggregateType(Id typeId)    const
290         { return isArrayType(typeId) || isStructType(typeId) || isCooperativeMatrixType(typeId); }
isImageType(Id typeId)291     bool isImageType(Id typeId)        const { return getTypeClass(typeId) == OpTypeImage; }
isSamplerType(Id typeId)292     bool isSamplerType(Id typeId)      const { return getTypeClass(typeId) == OpTypeSampler; }
isSampledImageType(Id typeId)293     bool isSampledImageType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampledImage; }
294     bool containsType(Id typeId, Op typeOp, unsigned int width) const;
295     bool containsPhysicalStorageBufferOrArray(Id typeId) const;
296 
297     bool isConstantOpCode(Op opcode) const;
298     bool isSpecConstantOpCode(Op opcode) const;
isConstant(Id resultId)299     bool isConstant(Id resultId) const { return isConstantOpCode(getOpCode(resultId)); }
isConstantScalar(Id resultId)300     bool isConstantScalar(Id resultId) const { return getOpCode(resultId) == OpConstant; }
isSpecConstant(Id resultId)301     bool isSpecConstant(Id resultId) const { return isSpecConstantOpCode(getOpCode(resultId)); }
getConstantScalar(Id resultId)302     unsigned int getConstantScalar(Id resultId) const
303         { return module.getInstruction(resultId)->getImmediateOperand(0); }
getStorageClass(Id resultId)304     StorageClass getStorageClass(Id resultId) const { return getTypeStorageClass(getTypeId(resultId)); }
305 
isVariableOpCode(Op opcode)306     bool isVariableOpCode(Op opcode) const { return opcode == OpVariable; }
isVariable(Id resultId)307     bool isVariable(Id resultId) const { return isVariableOpCode(getOpCode(resultId)); }
isGlobalStorage(Id resultId)308     bool isGlobalStorage(Id resultId) const { return getStorageClass(resultId) != StorageClassFunction; }
isGlobalVariable(Id resultId)309     bool isGlobalVariable(Id resultId) const { return isVariable(resultId) && isGlobalStorage(resultId); }
310     // See if a resultId is valid for use as an initializer.
isValidInitializer(Id resultId)311     bool isValidInitializer(Id resultId) const { return isConstant(resultId) || isGlobalVariable(resultId); }
312 
313     bool isRayTracingOpCode(Op opcode) const;
314 
getScalarTypeWidth(Id typeId)315     int getScalarTypeWidth(Id typeId) const
316     {
317         Id scalarTypeId = getScalarTypeId(typeId);
318         assert(getTypeClass(scalarTypeId) == OpTypeInt || getTypeClass(scalarTypeId) == OpTypeFloat);
319         return module.getInstruction(scalarTypeId)->getImmediateOperand(0);
320     }
321 
getTypeNumColumns(Id typeId)322     int getTypeNumColumns(Id typeId) const
323     {
324         assert(isMatrixType(typeId));
325         return getNumTypeConstituents(typeId);
326     }
getNumColumns(Id resultId)327     int getNumColumns(Id resultId) const { return getTypeNumColumns(getTypeId(resultId)); }
getTypeNumRows(Id typeId)328     int getTypeNumRows(Id typeId) const
329     {
330         assert(isMatrixType(typeId));
331         return getNumTypeComponents(getContainedTypeId(typeId));
332     }
getNumRows(Id resultId)333     int getNumRows(Id resultId) const { return getTypeNumRows(getTypeId(resultId)); }
334 
getTypeDimensionality(Id typeId)335     Dim getTypeDimensionality(Id typeId) const
336     {
337         assert(isImageType(typeId));
338         return (Dim)module.getInstruction(typeId)->getImmediateOperand(1);
339     }
getImageType(Id resultId)340     Id getImageType(Id resultId) const
341     {
342         Id typeId = getTypeId(resultId);
343         assert(isImageType(typeId) || isSampledImageType(typeId));
344         return isSampledImageType(typeId) ? module.getInstruction(typeId)->getIdOperand(0) : typeId;
345     }
isArrayedImageType(Id typeId)346     bool isArrayedImageType(Id typeId) const
347     {
348         assert(isImageType(typeId));
349         return module.getInstruction(typeId)->getImmediateOperand(3) != 0;
350     }
351 
352     // For making new constants (will return old constant if the requested one was already made).
353     Id makeNullConstant(Id typeId);
354     Id makeBoolConstant(bool b, bool specConstant = false);
355     Id makeInt8Constant(int i, bool specConstant = false)
356         { return makeIntConstant(makeIntType(8),  (unsigned)i, specConstant); }
357     Id makeUint8Constant(unsigned u, bool specConstant = false)
358         { return makeIntConstant(makeUintType(8),           u, specConstant); }
359     Id makeInt16Constant(int i, bool specConstant = false)
360         { return makeIntConstant(makeIntType(16),  (unsigned)i, specConstant); }
361     Id makeUint16Constant(unsigned u, bool specConstant = false)
362         { return makeIntConstant(makeUintType(16),           u, specConstant); }
363     Id makeIntConstant(int i, bool specConstant = false)
364         { return makeIntConstant(makeIntType(32),  (unsigned)i, specConstant); }
365     Id makeUintConstant(unsigned u, bool specConstant = false)
366         { return makeIntConstant(makeUintType(32),           u, specConstant); }
367     Id makeInt64Constant(long long i, bool specConstant = false)
368         { return makeInt64Constant(makeIntType(64),  (unsigned long long)i, specConstant); }
369     Id makeUint64Constant(unsigned long long u, bool specConstant = false)
370         { return makeInt64Constant(makeUintType(64),                     u, specConstant); }
371     Id makeFloatConstant(float f, bool specConstant = false);
372     Id makeDoubleConstant(double d, bool specConstant = false);
373     Id makeFloat16Constant(float f16, bool specConstant = false);
374     Id makeFpConstant(Id type, double d, bool specConstant = false);
375 
376     Id importNonSemanticShaderDebugInfoInstructions();
377 
378     // Turn the array of constants into a proper spv constant of the requested type.
379     Id makeCompositeConstant(Id type, const std::vector<Id>& comps, bool specConst = false);
380 
381     // Methods for adding information outside the CFG.
382     Instruction* addEntryPoint(ExecutionModel, Function*, const char* name);
383     void addExecutionMode(Function*, ExecutionMode mode, int value1 = -1, int value2 = -1, int value3 = -1);
384     void addExecutionMode(Function*, ExecutionMode mode, const std::vector<unsigned>& literals);
385     void addExecutionModeId(Function*, ExecutionMode mode, const std::vector<Id>& operandIds);
386     void addName(Id, const char* name);
387     void addMemberName(Id, int member, const char* name);
388     void addDecoration(Id, Decoration, int num = -1);
389     void addDecoration(Id, Decoration, const char*);
390     void addDecoration(Id, Decoration, const std::vector<unsigned>& literals);
391     void addDecoration(Id, Decoration, const std::vector<const char*>& strings);
392     void addDecorationId(Id id, Decoration, Id idDecoration);
393     void addDecorationId(Id id, Decoration, const std::vector<Id>& operandIds);
394     void addMemberDecoration(Id, unsigned int member, Decoration, int num = -1);
395     void addMemberDecoration(Id, unsigned int member, Decoration, const char*);
396     void addMemberDecoration(Id, unsigned int member, Decoration, const std::vector<unsigned>& literals);
397     void addMemberDecoration(Id, unsigned int member, Decoration, const std::vector<const char*>& strings);
398 
399     // At the end of what block do the next create*() instructions go?
400     // Also reset current last DebugScope and current source line to unknown
setBuildPoint(Block * bp)401     void setBuildPoint(Block* bp) {
402         buildPoint = bp;
403         lastDebugScopeId = NoResult;
404         currentLine = 0;
405     }
getBuildPoint()406     Block* getBuildPoint() const { return buildPoint; }
407 
408     // Make the entry-point function. The returned pointer is only valid
409     // for the lifetime of this builder.
410     Function* makeEntryPoint(const char*);
411 
412     // Make a shader-style function, and create its entry block if entry is non-zero.
413     // Return the function, pass back the entry.
414     // The returned pointer is only valid for the lifetime of this builder.
415     Function* makeFunctionEntry(Decoration precision, Id returnType, const char* name,
416         const std::vector<Id>& paramTypes, const std::vector<char const*>& paramNames,
417         const std::vector<std::vector<Decoration>>& precisions, Block **entry = nullptr);
418 
419     // Create a return. An 'implicit' return is one not appearing in the source
420     // code.  In the case of an implicit return, no post-return block is inserted.
421     void makeReturn(bool implicit, Id retVal = 0);
422 
423     // Initialize state and generate instructions for new lexical scope
424     void enterScope(uint32_t line);
425 
426     // Set state and generate instructions to exit current lexical scope
427     void leaveScope();
428 
429     // Prepare builder for generation of instructions for a function.
430     void enterFunction(Function const* function);
431 
432     // Generate all the code needed to finish up a function.
433     void leaveFunction();
434 
435     // Create block terminator instruction for certain statements like
436     // discard, terminate-invocation, terminateRayEXT, or ignoreIntersectionEXT
437     void makeStatementTerminator(spv::Op opcode, const char *name);
438 
439     // Create block terminator instruction for statements that have input operands
440     // such as OpEmitMeshTasksEXT
441     void makeStatementTerminator(spv::Op opcode, const std::vector<Id>& operands, const char* name);
442 
443     // Create a global or function local or IO variable.
444     Id createVariable(Decoration precision, StorageClass storageClass, Id type, const char* name = nullptr,
445         Id initializer = NoResult, bool const compilerGenerated = true);
446 
447     // Create an intermediate with an undefined value.
448     Id createUndefined(Id type);
449 
450     // Store into an Id and return the l-value
451     void createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone,
452         spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);
453 
454     // Load from an Id and return it
455     Id createLoad(Id lValue, spv::Decoration precision,
456         spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone,
457         spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);
458 
459     // Create an OpAccessChain instruction
460     Id createAccessChain(StorageClass, Id base, const std::vector<Id>& offsets);
461 
462     // Create an OpArrayLength instruction
463     Id createArrayLength(Id base, unsigned int member);
464 
465     // Create an OpCooperativeMatrixLengthNV instruction
466     Id createCooperativeMatrixLength(Id type);
467 
468     // Create an OpCompositeExtract instruction
469     Id createCompositeExtract(Id composite, Id typeId, unsigned index);
470     Id createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes);
471     Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index);
472     Id createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes);
473 
474     Id createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex);
475     Id createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex);
476 
477     void createNoResultOp(Op);
478     void createNoResultOp(Op, Id operand);
479     void createNoResultOp(Op, const std::vector<Id>& operands);
480     void createNoResultOp(Op, const std::vector<IdImmediate>& operands);
481     void createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask);
482     void createMemoryBarrier(unsigned executionScope, unsigned memorySemantics);
483     Id createUnaryOp(Op, Id typeId, Id operand);
484     Id createBinOp(Op, Id typeId, Id operand1, Id operand2);
485     Id createTriOp(Op, Id typeId, Id operand1, Id operand2, Id operand3);
486     Id createOp(Op, Id typeId, const std::vector<Id>& operands);
487     Id createOp(Op, Id typeId, const std::vector<IdImmediate>& operands);
488     Id createFunctionCall(spv::Function*, const std::vector<spv::Id>&);
489     Id createSpecConstantOp(Op, Id typeId, const std::vector<spv::Id>& operands, const std::vector<unsigned>& literals);
490 
491     // Take an rvalue (source) and a set of channels to extract from it to
492     // make a new rvalue, which is returned.
493     Id createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels);
494 
495     // Take a copy of an lvalue (target) and a source of components, and set the
496     // source components into the lvalue where the 'channels' say to put them.
497     // An updated version of the target is returned.
498     // (No true lvalue or stores are used.)
499     Id createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels);
500 
501     // If both the id and precision are valid, the id
502     // gets tagged with the requested precision.
503     // The passed in id is always the returned id, to simplify use patterns.
setPrecision(Id id,Decoration precision)504     Id setPrecision(Id id, Decoration precision)
505     {
506         if (precision != NoPrecision && id != NoResult)
507             addDecoration(id, precision);
508 
509         return id;
510     }
511 
512     // Can smear a scalar to a vector for the following forms:
513     //   - promoteScalar(scalar, vector)  // smear scalar to width of vector
514     //   - promoteScalar(vector, scalar)  // smear scalar to width of vector
515     //   - promoteScalar(pointer, scalar) // smear scalar to width of what pointer points to
516     //   - promoteScalar(scalar, scalar)  // do nothing
517     // Other forms are not allowed.
518     //
519     // Generally, the type of 'scalar' does not need to be the same type as the components in 'vector'.
520     // The type of the created vector is a vector of components of the same type as the scalar.
521     //
522     // Note: One of the arguments will change, with the result coming back that way rather than
523     // through the return value.
524     void promoteScalar(Decoration precision, Id& left, Id& right);
525 
526     // Make a value by smearing the scalar to fill the type.
527     // vectorType should be the correct type for making a vector of scalarVal.
528     // (No conversions are done.)
529     Id smearScalar(Decoration precision, Id scalarVal, Id vectorType);
530 
531     // Create a call to a built-in function.
532     Id createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args);
533 
534     // List of parameters used to create a texture operation
535     struct TextureParameters {
536         Id sampler;
537         Id coords;
538         Id bias;
539         Id lod;
540         Id Dref;
541         Id offset;
542         Id offsets;
543         Id gradX;
544         Id gradY;
545         Id sample;
546         Id component;
547         Id texelOut;
548         Id lodClamp;
549         Id granularity;
550         Id coarse;
551         bool nonprivate;
552         bool volatil;
553     };
554 
555     // Select the correct texture operation based on all inputs, and emit the correct instruction
556     Id createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather,
557         bool noImplicit, const TextureParameters&, ImageOperandsMask);
558 
559     // Emit the OpTextureQuery* instruction that was passed in.
560     // Figure out the right return value and type, and return it.
561     Id createTextureQueryCall(Op, const TextureParameters&, bool isUnsignedResult);
562 
563     Id createSamplePositionCall(Decoration precision, Id, Id);
564 
565     Id createBitFieldExtractCall(Decoration precision, Id, Id, Id, bool isSigned);
566     Id createBitFieldInsertCall(Decoration precision, Id, Id, Id, Id);
567 
568     // Reduction comparison for composites:  For equal and not-equal resulting in a scalar.
569     Id createCompositeCompare(Decoration precision, Id, Id, bool /* true if for equal, false if for not-equal */);
570 
571     // OpCompositeConstruct
572     Id createCompositeConstruct(Id typeId, const std::vector<Id>& constituents);
573 
574     // vector or scalar constructor
575     Id createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId);
576 
577     // matrix constructor
578     Id createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id constructee);
579 
580     // Helper to use for building nested control flow with if-then-else.
581     class If {
582     public:
583         If(Id condition, unsigned int ctrl, Builder& builder);
~If()584         ~If() {}
585 
586         void makeBeginElse();
587         void makeEndIf();
588 
589     private:
590         If(const If&);
591         If& operator=(If&);
592 
593         Builder& builder;
594         Id condition;
595         unsigned int control;
596         Function* function;
597         Block* headerBlock;
598         Block* thenBlock;
599         Block* elseBlock;
600         Block* mergeBlock;
601     };
602 
603     // Make a switch statement.  A switch has 'numSegments' of pieces of code, not containing
604     // any case/default labels, all separated by one or more case/default labels.  Each possible
605     // case value v is a jump to the caseValues[v] segment.  The defaultSegment is also in this
606     // number space.  How to compute the value is given by 'condition', as in switch(condition).
607     //
608     // The SPIR-V Builder will maintain the stack of post-switch merge blocks for nested switches.
609     //
610     // Use a defaultSegment < 0 if there is no default segment (to branch to post switch).
611     //
612     // Returns the right set of basic blocks to start each code segment with, so that the caller's
613     // recursion stack can hold the memory for it.
614     //
615     void makeSwitch(Id condition, unsigned int control, int numSegments, const std::vector<int>& caseValues,
616                     const std::vector<int>& valueToSegment, int defaultSegment, std::vector<Block*>& segmentBB);
617 
618     // Add a branch to the innermost switch's merge block.
619     void addSwitchBreak();
620 
621     // Move to the next code segment, passing in the return argument in makeSwitch()
622     void nextSwitchSegment(std::vector<Block*>& segmentBB, int segment);
623 
624     // Finish off the innermost switch.
625     void endSwitch(std::vector<Block*>& segmentBB);
626 
627     struct LoopBlocks {
LoopBlocksLoopBlocks628         LoopBlocks(Block& head, Block& body, Block& merge, Block& continue_target) :
629             head(head), body(body), merge(merge), continue_target(continue_target) { }
630         Block &head, &body, &merge, &continue_target;
631     private:
632         LoopBlocks();
633         LoopBlocks& operator=(const LoopBlocks&) = delete;
634     };
635 
636     // Start a new loop and prepare the builder to generate code for it.  Until
637     // closeLoop() is called for this loop, createLoopContinue() and
638     // createLoopExit() will target its corresponding blocks.
639     LoopBlocks& makeNewLoop();
640 
641     // Create a new block in the function containing the build point.  Memory is
642     // owned by the function object.
643     Block& makeNewBlock();
644 
645     // Add a branch to the continue_target of the current (innermost) loop.
646     void createLoopContinue();
647 
648     // Add an exit (e.g. "break") from the innermost loop that we're currently
649     // in.
650     void createLoopExit();
651 
652     // Close the innermost loop that you're in
653     void closeLoop();
654 
655     //
656     // Access chain design for an R-Value vs. L-Value:
657     //
658     // There is a single access chain the builder is building at
659     // any particular time.  Such a chain can be used to either to a load or
660     // a store, when desired.
661     //
662     // Expressions can be r-values, l-values, or both, or only r-values:
663     //    a[b.c].d = ....  // l-value
664     //    ... = a[b.c].d;  // r-value, that also looks like an l-value
665     //    ++a[b.c].d;      // r-value and l-value
666     //    (x + y)[2];      // r-value only, can't possibly be l-value
667     //
668     // Computing an r-value means generating code.  Hence,
669     // r-values should only be computed when they are needed, not speculatively.
670     //
671     // Computing an l-value means saving away information for later use in the compiler,
672     // no code is generated until the l-value is later dereferenced.  It is okay
673     // to speculatively generate an l-value, just not okay to speculatively dereference it.
674     //
675     // The base of the access chain (the left-most variable or expression
676     // from which everything is based) can be set either as an l-value
677     // or as an r-value.  Most efficient would be to set an l-value if one
678     // is available.  If an expression was evaluated, the resulting r-value
679     // can be set as the chain base.
680     //
681     // The users of this single access chain can save and restore if they
682     // want to nest or manage multiple chains.
683     //
684 
685     struct AccessChain {
686         Id base;                       // for l-values, pointer to the base object, for r-values, the base object
687         std::vector<Id> indexChain;
688         Id instr;                      // cache the instruction that generates this access chain
689         std::vector<unsigned> swizzle; // each std::vector element selects the next GLSL component number
690         Id component;                  // a dynamic component index, can coexist with a swizzle,
691                                        // done after the swizzle, NoResult if not present
692         Id preSwizzleBaseType;         // dereferenced type, before swizzle or component is applied;
693                                        // NoType unless a swizzle or component is present
694         bool isRValue;                 // true if 'base' is an r-value, otherwise, base is an l-value
695         unsigned int alignment;        // bitwise OR of alignment values passed in. Accumulates worst alignment.
696                                        // Only tracks base and (optional) component selection alignment.
697 
698         // Accumulate whether anything in the chain of structures has coherent decorations.
699         struct CoherentFlags {
CoherentFlagsAccessChain::CoherentFlags700             CoherentFlags() { clear(); }
701 #ifdef GLSLANG_WEB
clearAccessChain::CoherentFlags702             void clear() { }
isVolatileAccessChain::CoherentFlags703             bool isVolatile() const { return false; }
704             CoherentFlags operator |=(const CoherentFlags &other) { return *this; }
705 #else
isVolatileAccessChain::CoherentFlags706             bool isVolatile() const { return volatil; }
isNonUniformAccessChain::CoherentFlags707             bool isNonUniform() const { return nonUniform; }
anyCoherentAccessChain::CoherentFlags708             bool anyCoherent() const {
709                 return coherent || devicecoherent || queuefamilycoherent || workgroupcoherent ||
710                     subgroupcoherent || shadercallcoherent;
711             }
712 
713             unsigned coherent : 1;
714             unsigned devicecoherent : 1;
715             unsigned queuefamilycoherent : 1;
716             unsigned workgroupcoherent : 1;
717             unsigned subgroupcoherent : 1;
718             unsigned shadercallcoherent : 1;
719             unsigned nonprivate : 1;
720             unsigned volatil : 1;
721             unsigned isImage : 1;
722             unsigned nonUniform : 1;
723 
clearAccessChain::CoherentFlags724             void clear() {
725                 coherent = 0;
726                 devicecoherent = 0;
727                 queuefamilycoherent = 0;
728                 workgroupcoherent = 0;
729                 subgroupcoherent = 0;
730                 shadercallcoherent = 0;
731                 nonprivate = 0;
732                 volatil = 0;
733                 isImage = 0;
734                 nonUniform = 0;
735             }
736 
737             CoherentFlags operator |=(const CoherentFlags &other) {
738                 coherent |= other.coherent;
739                 devicecoherent |= other.devicecoherent;
740                 queuefamilycoherent |= other.queuefamilycoherent;
741                 workgroupcoherent |= other.workgroupcoherent;
742                 subgroupcoherent |= other.subgroupcoherent;
743                 shadercallcoherent |= other.shadercallcoherent;
744                 nonprivate |= other.nonprivate;
745                 volatil |= other.volatil;
746                 isImage |= other.isImage;
747                 nonUniform |= other.nonUniform;
748                 return *this;
749             }
750 #endif
751         };
752         CoherentFlags coherentFlags;
753     };
754 
755     //
756     // the SPIR-V builder maintains a single active chain that
757     // the following methods operate on
758     //
759 
760     // for external save and restore
getAccessChain()761     AccessChain getAccessChain() { return accessChain; }
setAccessChain(AccessChain newChain)762     void setAccessChain(AccessChain newChain) { accessChain = newChain; }
763 
764     // clear accessChain
765     void clearAccessChain();
766 
767     // set new base as an l-value base
setAccessChainLValue(Id lValue)768     void setAccessChainLValue(Id lValue)
769     {
770         assert(isPointer(lValue));
771         accessChain.base = lValue;
772     }
773 
774     // set new base value as an r-value
setAccessChainRValue(Id rValue)775     void setAccessChainRValue(Id rValue)
776     {
777         accessChain.isRValue = true;
778         accessChain.base = rValue;
779     }
780 
781     // push offset onto the end of the chain
accessChainPush(Id offset,AccessChain::CoherentFlags coherentFlags,unsigned int alignment)782     void accessChainPush(Id offset, AccessChain::CoherentFlags coherentFlags, unsigned int alignment)
783     {
784         accessChain.indexChain.push_back(offset);
785         accessChain.coherentFlags |= coherentFlags;
786         accessChain.alignment |= alignment;
787     }
788 
789     // push new swizzle onto the end of any existing swizzle, merging into a single swizzle
790     void accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType,
791         AccessChain::CoherentFlags coherentFlags, unsigned int alignment);
792 
793     // push a dynamic component selection onto the access chain, only applicable with a
794     // non-trivial swizzle or no swizzle
accessChainPushComponent(Id component,Id preSwizzleBaseType,AccessChain::CoherentFlags coherentFlags,unsigned int alignment)795     void accessChainPushComponent(Id component, Id preSwizzleBaseType, AccessChain::CoherentFlags coherentFlags,
796         unsigned int alignment)
797     {
798         if (accessChain.swizzle.size() != 1) {
799             accessChain.component = component;
800             if (accessChain.preSwizzleBaseType == NoType)
801                 accessChain.preSwizzleBaseType = preSwizzleBaseType;
802         }
803         accessChain.coherentFlags |= coherentFlags;
804         accessChain.alignment |= alignment;
805     }
806 
807     // use accessChain and swizzle to store value
808     void accessChainStore(Id rvalue, Decoration nonUniform,
809         spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone,
810         spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);
811 
812     // use accessChain and swizzle to load an r-value
813     Id accessChainLoad(Decoration precision, Decoration l_nonUniform, Decoration r_nonUniform, Id ResultType,
814         spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax,
815             unsigned int alignment = 0);
816 
817     // Return whether or not the access chain can be represented in SPIR-V
818     // as an l-value.
819     // E.g., a[3].yx cannot be, while a[3].y and a[3].y[x] can be.
isSpvLvalue()820     bool isSpvLvalue() const { return accessChain.swizzle.size() <= 1; }
821 
822     // get the direct pointer for an l-value
823     Id accessChainGetLValue();
824 
825     // Get the inferred SPIR-V type of the result of the current access chain,
826     // based on the type of the base and the chain of dereferences.
827     Id accessChainGetInferredType();
828 
829     // Add capabilities, extensions, remove unneeded decorations, etc.,
830     // based on the resulting SPIR-V.
831     void postProcess();
832 
833     // Prune unreachable blocks in the CFG and remove unneeded decorations.
834     void postProcessCFG();
835 
836 #ifndef GLSLANG_WEB
837     // Add capabilities, extensions based on instructions in the module.
838     void postProcessFeatures();
839     // Hook to visit each instruction in a block in a function
840     void postProcess(Instruction&);
841     // Hook to visit each non-32-bit sized float/int operation in a block.
842     void postProcessType(const Instruction&, spv::Id typeId);
843 #endif
844 
845     void dump(std::vector<unsigned int>&) const;
846 
847     void createBranch(Block* block);
848     void createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock);
849     void createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control,
850         const std::vector<unsigned int>& operands);
851 
852     // Sets to generate opcode for specialization constants.
setToSpecConstCodeGenMode()853     void setToSpecConstCodeGenMode() { generatingOpCodeForSpecConst = true; }
854     // Sets to generate opcode for non-specialization constants (normal mode).
setToNormalCodeGenMode()855     void setToNormalCodeGenMode() { generatingOpCodeForSpecConst = false; }
856     // Check if the builder is generating code for spec constants.
isInSpecConstCodeGenMode()857     bool isInSpecConstCodeGenMode() { return generatingOpCodeForSpecConst; }
858 
859  protected:
860     Id makeIntConstant(Id typeId, unsigned value, bool specConstant);
861     Id makeInt64Constant(Id typeId, unsigned long long value, bool specConstant);
862     Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value);
863     Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2);
864     Id findCompositeConstant(Op typeClass, Id typeId, const std::vector<Id>& comps);
865     Id findStructConstant(Id typeId, const std::vector<Id>& comps);
866     Id collapseAccessChain();
867     void remapDynamicSwizzle();
868     void transferAccessChainSwizzle(bool dynamic);
869     void simplifyAccessChainSwizzle();
870     void createAndSetNoPredecessorBlock(const char*);
871     void createSelectionMerge(Block* mergeBlock, unsigned int control);
872     void dumpSourceInstructions(std::vector<unsigned int>&) const;
873     void dumpSourceInstructions(const spv::Id fileId, const std::string& text, std::vector<unsigned int>&) const;
874     void dumpInstructions(std::vector<unsigned int>&, const std::vector<std::unique_ptr<Instruction> >&) const;
875     void dumpModuleProcesses(std::vector<unsigned int>&) const;
876     spv::MemoryAccessMask sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc)
877         const;
878 
879     unsigned int spvVersion;     // the version of SPIR-V to emit in the header
880     SourceLanguage sourceLang;
881     int sourceVersion;
882     spv::Id sourceFileStringId;
883     spv::Id nonSemanticShaderCompilationUnitId {0};
884     spv::Id nonSemanticShaderDebugInfo {0};
885     spv::Id debugInfoNone {0};
886     spv::Id debugExpression {0}; // Debug expression with zero operations.
887     std::string sourceText;
888     int currentLine;
889     const char* currentFile;
890     spv::Id currentFileId;
891     std::stack<spv::Id> currentDebugScopeId;
892     spv::Id lastDebugScopeId;
893     bool emitOpLines;
894     bool emitNonSemanticShaderDebugInfo;
895     bool restoreNonSemanticShaderDebugInfo;
896     bool emitNonSemanticShaderDebugSource;
897     std::set<std::string> extensions;
898     std::vector<const char*> sourceExtensions;
899     std::vector<const char*> moduleProcesses;
900     AddressingModel addressModel;
901     MemoryModel memoryModel;
902     std::set<spv::Capability> capabilities;
903     int builderNumber;
904     Module module;
905     Block* buildPoint;
906     Id uniqueId;
907     Function* entryPointFunction;
908     bool generatingOpCodeForSpecConst;
909     AccessChain accessChain;
910 
911     // special blocks of instructions for output
912     std::vector<std::unique_ptr<Instruction> > strings;
913     std::vector<std::unique_ptr<Instruction> > imports;
914     std::vector<std::unique_ptr<Instruction> > entryPoints;
915     std::vector<std::unique_ptr<Instruction> > executionModes;
916     std::vector<std::unique_ptr<Instruction> > names;
917     std::vector<std::unique_ptr<Instruction> > decorations;
918     std::vector<std::unique_ptr<Instruction> > constantsTypesGlobals;
919     std::vector<std::unique_ptr<Instruction> > externals;
920     std::vector<std::unique_ptr<Function> > functions;
921 
922     // not output, internally used for quick & dirty canonical (unique) creation
923 
924     // map type opcodes to constant inst.
925     std::unordered_map<unsigned int, std::vector<Instruction*>> groupedConstants;
926     // map struct-id to constant instructions
927     std::unordered_map<unsigned int, std::vector<Instruction*>> groupedStructConstants;
928     // map type opcodes to type instructions
929     std::unordered_map<unsigned int, std::vector<Instruction*>> groupedTypes;
930     // map type opcodes to debug type instructions
931     std::unordered_map<unsigned int, std::vector<Instruction*>> groupedDebugTypes;
932     // list of OpConstantNull instructions
933     std::vector<Instruction*> nullConstants;
934 
935     // stack of switches
936     std::stack<Block*> switchMerges;
937 
938     // Our loop stack.
939     std::stack<LoopBlocks> loops;
940 
941     // map from strings to their string ids
942     std::unordered_map<std::string, spv::Id> stringIds;
943 
944     // map from include file name ids to their contents
945     std::map<spv::Id, const std::string*> includeFiles;
946 
947     // map from core id to debug id
948     std::map <spv::Id, spv::Id> debugId;
949 
950     // map from file name string id to DebugSource id
951     std::unordered_map<spv::Id, spv::Id> debugSourceId;
952 
953     // The stream for outputting warnings and errors.
954     SpvBuildLogger* logger;
955 };  // end Builder class
956 
957 };  // end spv namespace
958 
959 #endif // SpvBuilder_H
960