• 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();
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 makeCooperativeMatrixTypeKHR(Id component, Id scope, Id rows, Id cols, Id use);
207     Id makeCooperativeMatrixTypeNV(Id component, Id scope, Id rows, Id cols);
208     Id makeCooperativeMatrixTypeWithSameShape(Id component, Id otherType);
209     Id makeGenericType(spv::Op opcode, std::vector<spv::IdImmediate>& operands);
210 
211     // SPIR-V NonSemantic Shader DebugInfo Instructions
212     struct DebugTypeLoc {
213         std::string name {};
214         int line {0};
215         int column {0};
216     };
217     std::unordered_map<Id, DebugTypeLoc> debugTypeLocs;
218     Id makeDebugInfoNone();
219     Id makeBoolDebugType(int const size);
220     Id makeIntegerDebugType(int const width, bool const hasSign);
221     Id makeFloatDebugType(int const width);
222     Id makeSequentialDebugType(Id const baseType, Id const componentCount, NonSemanticShaderDebugInfo100Instructions const sequenceType);
223     Id makeArrayDebugType(Id const baseType, Id const componentCount);
224     Id makeVectorDebugType(Id const baseType, int const componentCount);
225     Id makeMatrixDebugType(Id const vectorType, int const vectorCount, bool columnMajor = true);
226     Id makeMemberDebugType(Id const memberType, DebugTypeLoc const& debugTypeLoc);
227     Id makeCompositeDebugType(std::vector<Id> const& memberTypes, char const*const name,
228         NonSemanticShaderDebugInfo100DebugCompositeType const tag, bool const isOpaqueType = false);
229     Id makeDebugSource(const Id fileName);
230     Id makeDebugCompilationUnit();
231     Id createDebugGlobalVariable(Id const type, char const*const name, Id const variable);
232     Id createDebugLocalVariable(Id type, char const*const name, size_t const argNumber = 0);
233     Id makeDebugExpression();
234     Id makeDebugDeclare(Id const debugLocalVariable, Id const pointer);
235     Id makeDebugValue(Id const debugLocalVariable, Id const value);
236     Id makeDebugFunctionType(Id returnType, const std::vector<Id>& paramTypes);
237     Id makeDebugFunction(Function* function, Id nameId, Id funcTypeId);
238     Id makeDebugLexicalBlock(uint32_t line);
239     std::string unmangleFunctionName(std::string const& name) const;
240     void setupDebugFunctionEntry(Function* function, const char* name, int line,
241                                  const std::vector<Id>& paramTypes,
242                                  const std::vector<char const*>& paramNames);
243 
244     // accelerationStructureNV type
245     Id makeAccelerationStructureType();
246     // rayQueryEXT type
247     Id makeRayQueryType();
248     // hitObjectNV type
249     Id makeHitObjectNVType();
250 
251     // For querying about types.
getTypeId(Id resultId)252     Id getTypeId(Id resultId) const { return module.getTypeId(resultId); }
253     Id getDerefTypeId(Id resultId) const;
getOpCode(Id id)254     Op getOpCode(Id id) const { return module.getInstruction(id)->getOpCode(); }
getTypeClass(Id typeId)255     Op getTypeClass(Id typeId) const { return getOpCode(typeId); }
256     Op getMostBasicTypeClass(Id typeId) const;
getNumComponents(Id resultId)257     int getNumComponents(Id resultId) const { return getNumTypeComponents(getTypeId(resultId)); }
258     int getNumTypeConstituents(Id typeId) const;
getNumTypeComponents(Id typeId)259     int getNumTypeComponents(Id typeId) const { return getNumTypeConstituents(typeId); }
260     Id getScalarTypeId(Id typeId) const;
261     Id getContainedTypeId(Id typeId) const;
262     Id getContainedTypeId(Id typeId, int) const;
getTypeStorageClass(Id typeId)263     StorageClass getTypeStorageClass(Id typeId) const { return module.getStorageClass(typeId); }
getImageTypeFormat(Id typeId)264     ImageFormat getImageTypeFormat(Id typeId) const
265         { return (ImageFormat)module.getInstruction(typeId)->getImmediateOperand(6); }
266     Id getResultingAccessChainType() const;
getIdOperand(Id resultId,int idx)267     Id getIdOperand(Id resultId, int idx) { return module.getInstruction(resultId)->getIdOperand(idx); }
268 
isPointer(Id resultId)269     bool isPointer(Id resultId)      const { return isPointerType(getTypeId(resultId)); }
isScalar(Id resultId)270     bool isScalar(Id resultId)       const { return isScalarType(getTypeId(resultId)); }
isVector(Id resultId)271     bool isVector(Id resultId)       const { return isVectorType(getTypeId(resultId)); }
isMatrix(Id resultId)272     bool isMatrix(Id resultId)       const { return isMatrixType(getTypeId(resultId)); }
isCooperativeMatrix(Id resultId)273     bool isCooperativeMatrix(Id resultId)const { return isCooperativeMatrixType(getTypeId(resultId)); }
isAggregate(Id resultId)274     bool isAggregate(Id resultId)    const { return isAggregateType(getTypeId(resultId)); }
isSampledImage(Id resultId)275     bool isSampledImage(Id resultId) const { return isSampledImageType(getTypeId(resultId)); }
276 
isBoolType(Id typeId)277     bool isBoolType(Id typeId)
278         { return groupedTypes[OpTypeBool].size() > 0 && typeId == groupedTypes[OpTypeBool].back()->getResultId(); }
isIntType(Id typeId)279     bool isIntType(Id typeId)          const
280         { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) != 0; }
isUintType(Id typeId)281     bool isUintType(Id typeId)         const
282         { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) == 0; }
isFloatType(Id typeId)283     bool isFloatType(Id typeId)        const { return getTypeClass(typeId) == OpTypeFloat; }
isPointerType(Id typeId)284     bool isPointerType(Id typeId)      const { return getTypeClass(typeId) == OpTypePointer; }
isScalarType(Id typeId)285     bool isScalarType(Id typeId)       const
286         { return getTypeClass(typeId) == OpTypeFloat || getTypeClass(typeId) == OpTypeInt ||
287           getTypeClass(typeId) == OpTypeBool; }
isVectorType(Id typeId)288     bool isVectorType(Id typeId)       const { return getTypeClass(typeId) == OpTypeVector; }
isMatrixType(Id typeId)289     bool isMatrixType(Id typeId)       const { return getTypeClass(typeId) == OpTypeMatrix; }
isStructType(Id typeId)290     bool isStructType(Id typeId)       const { return getTypeClass(typeId) == OpTypeStruct; }
isArrayType(Id typeId)291     bool isArrayType(Id typeId)        const { return getTypeClass(typeId) == OpTypeArray; }
isCooperativeMatrixType(Id typeId)292     bool isCooperativeMatrixType(Id typeId)const
293     {
294         return getTypeClass(typeId) == OpTypeCooperativeMatrixKHR || getTypeClass(typeId) == OpTypeCooperativeMatrixNV;
295     }
isAggregateType(Id typeId)296     bool isAggregateType(Id typeId)    const
297         { return isArrayType(typeId) || isStructType(typeId) || isCooperativeMatrixType(typeId); }
isImageType(Id typeId)298     bool isImageType(Id typeId)        const { return getTypeClass(typeId) == OpTypeImage; }
isSamplerType(Id typeId)299     bool isSamplerType(Id typeId)      const { return getTypeClass(typeId) == OpTypeSampler; }
isSampledImageType(Id typeId)300     bool isSampledImageType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampledImage; }
301     bool containsType(Id typeId, Op typeOp, unsigned int width) const;
302     bool containsPhysicalStorageBufferOrArray(Id typeId) const;
303 
304     bool isConstantOpCode(Op opcode) const;
305     bool isSpecConstantOpCode(Op opcode) const;
isConstant(Id resultId)306     bool isConstant(Id resultId) const { return isConstantOpCode(getOpCode(resultId)); }
isConstantScalar(Id resultId)307     bool isConstantScalar(Id resultId) const { return getOpCode(resultId) == OpConstant; }
isSpecConstant(Id resultId)308     bool isSpecConstant(Id resultId) const { return isSpecConstantOpCode(getOpCode(resultId)); }
getConstantScalar(Id resultId)309     unsigned int getConstantScalar(Id resultId) const
310         { return module.getInstruction(resultId)->getImmediateOperand(0); }
getStorageClass(Id resultId)311     StorageClass getStorageClass(Id resultId) const { return getTypeStorageClass(getTypeId(resultId)); }
312 
isVariableOpCode(Op opcode)313     bool isVariableOpCode(Op opcode) const { return opcode == OpVariable; }
isVariable(Id resultId)314     bool isVariable(Id resultId) const { return isVariableOpCode(getOpCode(resultId)); }
isGlobalStorage(Id resultId)315     bool isGlobalStorage(Id resultId) const { return getStorageClass(resultId) != StorageClassFunction; }
isGlobalVariable(Id resultId)316     bool isGlobalVariable(Id resultId) const { return isVariable(resultId) && isGlobalStorage(resultId); }
317     // See if a resultId is valid for use as an initializer.
isValidInitializer(Id resultId)318     bool isValidInitializer(Id resultId) const { return isConstant(resultId) || isGlobalVariable(resultId); }
319 
320     bool isRayTracingOpCode(Op opcode) const;
321 
getScalarTypeWidth(Id typeId)322     int getScalarTypeWidth(Id typeId) const
323     {
324         Id scalarTypeId = getScalarTypeId(typeId);
325         assert(getTypeClass(scalarTypeId) == OpTypeInt || getTypeClass(scalarTypeId) == OpTypeFloat);
326         return module.getInstruction(scalarTypeId)->getImmediateOperand(0);
327     }
328 
getTypeNumColumns(Id typeId)329     int getTypeNumColumns(Id typeId) const
330     {
331         assert(isMatrixType(typeId));
332         return getNumTypeConstituents(typeId);
333     }
getNumColumns(Id resultId)334     int getNumColumns(Id resultId) const { return getTypeNumColumns(getTypeId(resultId)); }
getTypeNumRows(Id typeId)335     int getTypeNumRows(Id typeId) const
336     {
337         assert(isMatrixType(typeId));
338         return getNumTypeComponents(getContainedTypeId(typeId));
339     }
getNumRows(Id resultId)340     int getNumRows(Id resultId) const { return getTypeNumRows(getTypeId(resultId)); }
341 
getTypeDimensionality(Id typeId)342     Dim getTypeDimensionality(Id typeId) const
343     {
344         assert(isImageType(typeId));
345         return (Dim)module.getInstruction(typeId)->getImmediateOperand(1);
346     }
getImageType(Id resultId)347     Id getImageType(Id resultId) const
348     {
349         Id typeId = getTypeId(resultId);
350         assert(isImageType(typeId) || isSampledImageType(typeId));
351         return isSampledImageType(typeId) ? module.getInstruction(typeId)->getIdOperand(0) : typeId;
352     }
isArrayedImageType(Id typeId)353     bool isArrayedImageType(Id typeId) const
354     {
355         assert(isImageType(typeId));
356         return module.getInstruction(typeId)->getImmediateOperand(3) != 0;
357     }
358 
359     // For making new constants (will return old constant if the requested one was already made).
360     Id makeNullConstant(Id typeId);
361     Id makeBoolConstant(bool b, bool specConstant = false);
362     Id makeInt8Constant(int i, bool specConstant = false)
363         { return makeIntConstant(makeIntType(8),  (unsigned)i, specConstant); }
364     Id makeUint8Constant(unsigned u, bool specConstant = false)
365         { return makeIntConstant(makeUintType(8),           u, specConstant); }
366     Id makeInt16Constant(int i, bool specConstant = false)
367         { return makeIntConstant(makeIntType(16),  (unsigned)i, specConstant); }
368     Id makeUint16Constant(unsigned u, bool specConstant = false)
369         { return makeIntConstant(makeUintType(16),           u, specConstant); }
370     Id makeIntConstant(int i, bool specConstant = false)
371         { return makeIntConstant(makeIntType(32),  (unsigned)i, specConstant); }
372     Id makeUintConstant(unsigned u, bool specConstant = false)
373         { return makeIntConstant(makeUintType(32),           u, specConstant); }
374     Id makeInt64Constant(long long i, bool specConstant = false)
375         { return makeInt64Constant(makeIntType(64),  (unsigned long long)i, specConstant); }
376     Id makeUint64Constant(unsigned long long u, bool specConstant = false)
377         { return makeInt64Constant(makeUintType(64),                     u, specConstant); }
378     Id makeFloatConstant(float f, bool specConstant = false);
379     Id makeDoubleConstant(double d, bool specConstant = false);
380     Id makeFloat16Constant(float f16, bool specConstant = false);
381     Id makeFpConstant(Id type, double d, bool specConstant = false);
382 
383     Id importNonSemanticShaderDebugInfoInstructions();
384 
385     // Turn the array of constants into a proper spv constant of the requested type.
386     Id makeCompositeConstant(Id type, const std::vector<Id>& comps, bool specConst = false);
387 
388     // Methods for adding information outside the CFG.
389     Instruction* addEntryPoint(ExecutionModel, Function*, const char* name);
390     void addExecutionMode(Function*, ExecutionMode mode, int value1 = -1, int value2 = -1, int value3 = -1);
391     void addExecutionMode(Function*, ExecutionMode mode, const std::vector<unsigned>& literals);
392     void addExecutionModeId(Function*, ExecutionMode mode, const std::vector<Id>& operandIds);
393     void addName(Id, const char* name);
394     void addMemberName(Id, int member, const char* name);
395     void addDecoration(Id, Decoration, int num = -1);
396     void addDecoration(Id, Decoration, const char*);
397     void addDecoration(Id, Decoration, const std::vector<unsigned>& literals);
398     void addDecoration(Id, Decoration, const std::vector<const char*>& strings);
399     void addLinkageDecoration(Id id, const char* name, spv::LinkageType linkType);
400     void addDecorationId(Id id, Decoration, Id idDecoration);
401     void addDecorationId(Id id, Decoration, const std::vector<Id>& operandIds);
402     void addMemberDecoration(Id, unsigned int member, Decoration, int num = -1);
403     void addMemberDecoration(Id, unsigned int member, Decoration, const char*);
404     void addMemberDecoration(Id, unsigned int member, Decoration, const std::vector<unsigned>& literals);
405     void addMemberDecoration(Id, unsigned int member, Decoration, const std::vector<const char*>& strings);
406 
407     // At the end of what block do the next create*() instructions go?
408     // Also reset current last DebugScope and current source line to unknown
setBuildPoint(Block * bp)409     void setBuildPoint(Block* bp) {
410         buildPoint = bp;
411         lastDebugScopeId = NoResult;
412         currentLine = 0;
413     }
getBuildPoint()414     Block* getBuildPoint() const { return buildPoint; }
415 
416     // Make the entry-point function. The returned pointer is only valid
417     // for the lifetime of this builder.
418     Function* makeEntryPoint(const char*);
419 
420     // Make a shader-style function, and create its entry block if entry is non-zero.
421     // Return the function, pass back the entry.
422     // The returned pointer is only valid for the lifetime of this builder.
423     Function* makeFunctionEntry(Decoration precision, Id returnType, const char* name, LinkageType linkType,
424                                 const std::vector<Id>& paramTypes,
425                                 const std::vector<std::vector<Decoration>>& precisions, Block** entry = nullptr);
426 
427     // Create a return. An 'implicit' return is one not appearing in the source
428     // code.  In the case of an implicit return, no post-return block is inserted.
429     void makeReturn(bool implicit, Id retVal = 0);
430 
431     // Initialize state and generate instructions for new lexical scope
432     void enterScope(uint32_t line);
433 
434     // Set state and generate instructions to exit current lexical scope
435     void leaveScope();
436 
437     // Prepare builder for generation of instructions for a function.
438     void enterFunction(Function const* function);
439 
440     // Generate all the code needed to finish up a function.
441     void leaveFunction();
442 
443     // Create block terminator instruction for certain statements like
444     // discard, terminate-invocation, terminateRayEXT, or ignoreIntersectionEXT
445     void makeStatementTerminator(spv::Op opcode, const char *name);
446 
447     // Create block terminator instruction for statements that have input operands
448     // such as OpEmitMeshTasksEXT
449     void makeStatementTerminator(spv::Op opcode, const std::vector<Id>& operands, const char* name);
450 
451     // Create a global or function local or IO variable.
452     Id createVariable(Decoration precision, StorageClass storageClass, Id type, const char* name = nullptr,
453         Id initializer = NoResult, bool const compilerGenerated = true);
454 
455     // Create an intermediate with an undefined value.
456     Id createUndefined(Id type);
457 
458     // Store into an Id and return the l-value
459     void createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone,
460         spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);
461 
462     // Load from an Id and return it
463     Id createLoad(Id lValue, spv::Decoration precision,
464         spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone,
465         spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);
466 
467     // Create an OpAccessChain instruction
468     Id createAccessChain(StorageClass, Id base, const std::vector<Id>& offsets);
469 
470     // Create an OpArrayLength instruction
471     Id createArrayLength(Id base, unsigned int member);
472 
473     // Create an OpCooperativeMatrixLengthKHR instruction
474     Id createCooperativeMatrixLengthKHR(Id type);
475     // Create an OpCooperativeMatrixLengthNV instruction
476     Id createCooperativeMatrixLengthNV(Id type);
477 
478     // Create an OpCompositeExtract instruction
479     Id createCompositeExtract(Id composite, Id typeId, unsigned index);
480     Id createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes);
481     Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index);
482     Id createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes);
483 
484     Id createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex);
485     Id createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex);
486 
487     void createNoResultOp(Op);
488     void createNoResultOp(Op, Id operand);
489     void createNoResultOp(Op, const std::vector<Id>& operands);
490     void createNoResultOp(Op, const std::vector<IdImmediate>& operands);
491     void createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask);
492     void createMemoryBarrier(unsigned executionScope, unsigned memorySemantics);
493     Id createUnaryOp(Op, Id typeId, Id operand);
494     Id createBinOp(Op, Id typeId, Id operand1, Id operand2);
495     Id createTriOp(Op, Id typeId, Id operand1, Id operand2, Id operand3);
496     Id createOp(Op, Id typeId, const std::vector<Id>& operands);
497     Id createOp(Op, Id typeId, const std::vector<IdImmediate>& operands);
498     Id createFunctionCall(spv::Function*, const std::vector<spv::Id>&);
499     Id createSpecConstantOp(Op, Id typeId, const std::vector<spv::Id>& operands, const std::vector<unsigned>& literals);
500 
501     // Take an rvalue (source) and a set of channels to extract from it to
502     // make a new rvalue, which is returned.
503     Id createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels);
504 
505     // Take a copy of an lvalue (target) and a source of components, and set the
506     // source components into the lvalue where the 'channels' say to put them.
507     // An updated version of the target is returned.
508     // (No true lvalue or stores are used.)
509     Id createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels);
510 
511     // If both the id and precision are valid, the id
512     // gets tagged with the requested precision.
513     // The passed in id is always the returned id, to simplify use patterns.
setPrecision(Id id,Decoration precision)514     Id setPrecision(Id id, Decoration precision)
515     {
516         if (precision != NoPrecision && id != NoResult)
517             addDecoration(id, precision);
518 
519         return id;
520     }
521 
522     // Can smear a scalar to a vector for the following forms:
523     //   - promoteScalar(scalar, vector)  // smear scalar to width of vector
524     //   - promoteScalar(vector, scalar)  // smear scalar to width of vector
525     //   - promoteScalar(pointer, scalar) // smear scalar to width of what pointer points to
526     //   - promoteScalar(scalar, scalar)  // do nothing
527     // Other forms are not allowed.
528     //
529     // Generally, the type of 'scalar' does not need to be the same type as the components in 'vector'.
530     // The type of the created vector is a vector of components of the same type as the scalar.
531     //
532     // Note: One of the arguments will change, with the result coming back that way rather than
533     // through the return value.
534     void promoteScalar(Decoration precision, Id& left, Id& right);
535 
536     // Make a value by smearing the scalar to fill the type.
537     // vectorType should be the correct type for making a vector of scalarVal.
538     // (No conversions are done.)
539     Id smearScalar(Decoration precision, Id scalarVal, Id vectorType);
540 
541     // Create a call to a built-in function.
542     Id createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args);
543 
544     // List of parameters used to create a texture operation
545     struct TextureParameters {
546         Id sampler;
547         Id coords;
548         Id bias;
549         Id lod;
550         Id Dref;
551         Id offset;
552         Id offsets;
553         Id gradX;
554         Id gradY;
555         Id sample;
556         Id component;
557         Id texelOut;
558         Id lodClamp;
559         Id granularity;
560         Id coarse;
561         bool nonprivate;
562         bool volatil;
563     };
564 
565     // Select the correct texture operation based on all inputs, and emit the correct instruction
566     Id createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather,
567         bool noImplicit, const TextureParameters&, ImageOperandsMask);
568 
569     // Emit the OpTextureQuery* instruction that was passed in.
570     // Figure out the right return value and type, and return it.
571     Id createTextureQueryCall(Op, const TextureParameters&, bool isUnsignedResult);
572 
573     Id createSamplePositionCall(Decoration precision, Id, Id);
574 
575     Id createBitFieldExtractCall(Decoration precision, Id, Id, Id, bool isSigned);
576     Id createBitFieldInsertCall(Decoration precision, Id, Id, Id, Id);
577 
578     // Reduction comparison for composites:  For equal and not-equal resulting in a scalar.
579     Id createCompositeCompare(Decoration precision, Id, Id, bool /* true if for equal, false if for not-equal */);
580 
581     // OpCompositeConstruct
582     Id createCompositeConstruct(Id typeId, const std::vector<Id>& constituents);
583 
584     // vector or scalar constructor
585     Id createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId);
586 
587     // matrix constructor
588     Id createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id constructee);
589 
590     // Helper to use for building nested control flow with if-then-else.
591     class If {
592     public:
593         If(Id condition, unsigned int ctrl, Builder& builder);
~If()594         ~If() {}
595 
596         void makeBeginElse();
597         void makeEndIf();
598 
599     private:
600         If(const If&);
601         If& operator=(If&);
602 
603         Builder& builder;
604         Id condition;
605         unsigned int control;
606         Function* function;
607         Block* headerBlock;
608         Block* thenBlock;
609         Block* elseBlock;
610         Block* mergeBlock;
611     };
612 
613     // Make a switch statement.  A switch has 'numSegments' of pieces of code, not containing
614     // any case/default labels, all separated by one or more case/default labels.  Each possible
615     // case value v is a jump to the caseValues[v] segment.  The defaultSegment is also in this
616     // number space.  How to compute the value is given by 'condition', as in switch(condition).
617     //
618     // The SPIR-V Builder will maintain the stack of post-switch merge blocks for nested switches.
619     //
620     // Use a defaultSegment < 0 if there is no default segment (to branch to post switch).
621     //
622     // Returns the right set of basic blocks to start each code segment with, so that the caller's
623     // recursion stack can hold the memory for it.
624     //
625     void makeSwitch(Id condition, unsigned int control, int numSegments, const std::vector<int>& caseValues,
626                     const std::vector<int>& valueToSegment, int defaultSegment, std::vector<Block*>& segmentBB);
627 
628     // Add a branch to the innermost switch's merge block.
629     void addSwitchBreak();
630 
631     // Move to the next code segment, passing in the return argument in makeSwitch()
632     void nextSwitchSegment(std::vector<Block*>& segmentBB, int segment);
633 
634     // Finish off the innermost switch.
635     void endSwitch(std::vector<Block*>& segmentBB);
636 
637     struct LoopBlocks {
LoopBlocksLoopBlocks638         LoopBlocks(Block& head, Block& body, Block& merge, Block& continue_target) :
639             head(head), body(body), merge(merge), continue_target(continue_target) { }
640         Block &head, &body, &merge, &continue_target;
641     private:
642         LoopBlocks();
643         LoopBlocks& operator=(const LoopBlocks&) = delete;
644     };
645 
646     // Start a new loop and prepare the builder to generate code for it.  Until
647     // closeLoop() is called for this loop, createLoopContinue() and
648     // createLoopExit() will target its corresponding blocks.
649     LoopBlocks& makeNewLoop();
650 
651     // Create a new block in the function containing the build point.  Memory is
652     // owned by the function object.
653     Block& makeNewBlock();
654 
655     // Add a branch to the continue_target of the current (innermost) loop.
656     void createLoopContinue();
657 
658     // Add an exit (e.g. "break") from the innermost loop that we're currently
659     // in.
660     void createLoopExit();
661 
662     // Close the innermost loop that you're in
663     void closeLoop();
664 
665     //
666     // Access chain design for an R-Value vs. L-Value:
667     //
668     // There is a single access chain the builder is building at
669     // any particular time.  Such a chain can be used to either to a load or
670     // a store, when desired.
671     //
672     // Expressions can be r-values, l-values, or both, or only r-values:
673     //    a[b.c].d = ....  // l-value
674     //    ... = a[b.c].d;  // r-value, that also looks like an l-value
675     //    ++a[b.c].d;      // r-value and l-value
676     //    (x + y)[2];      // r-value only, can't possibly be l-value
677     //
678     // Computing an r-value means generating code.  Hence,
679     // r-values should only be computed when they are needed, not speculatively.
680     //
681     // Computing an l-value means saving away information for later use in the compiler,
682     // no code is generated until the l-value is later dereferenced.  It is okay
683     // to speculatively generate an l-value, just not okay to speculatively dereference it.
684     //
685     // The base of the access chain (the left-most variable or expression
686     // from which everything is based) can be set either as an l-value
687     // or as an r-value.  Most efficient would be to set an l-value if one
688     // is available.  If an expression was evaluated, the resulting r-value
689     // can be set as the chain base.
690     //
691     // The users of this single access chain can save and restore if they
692     // want to nest or manage multiple chains.
693     //
694 
695     struct AccessChain {
696         Id base;                       // for l-values, pointer to the base object, for r-values, the base object
697         std::vector<Id> indexChain;
698         Id instr;                      // cache the instruction that generates this access chain
699         std::vector<unsigned> swizzle; // each std::vector element selects the next GLSL component number
700         Id component;                  // a dynamic component index, can coexist with a swizzle,
701                                        // done after the swizzle, NoResult if not present
702         Id preSwizzleBaseType;         // dereferenced type, before swizzle or component is applied;
703                                        // NoType unless a swizzle or component is present
704         bool isRValue;                 // true if 'base' is an r-value, otherwise, base is an l-value
705         unsigned int alignment;        // bitwise OR of alignment values passed in. Accumulates worst alignment.
706                                        // Only tracks base and (optional) component selection alignment.
707 
708         // Accumulate whether anything in the chain of structures has coherent decorations.
709         struct CoherentFlags {
CoherentFlagsAccessChain::CoherentFlags710             CoherentFlags() { clear(); }
isVolatileAccessChain::CoherentFlags711             bool isVolatile() const { return volatil; }
isNonUniformAccessChain::CoherentFlags712             bool isNonUniform() const { return nonUniform; }
anyCoherentAccessChain::CoherentFlags713             bool anyCoherent() const {
714                 return coherent || devicecoherent || queuefamilycoherent || workgroupcoherent ||
715                     subgroupcoherent || shadercallcoherent;
716             }
717 
718             unsigned coherent : 1;
719             unsigned devicecoherent : 1;
720             unsigned queuefamilycoherent : 1;
721             unsigned workgroupcoherent : 1;
722             unsigned subgroupcoherent : 1;
723             unsigned shadercallcoherent : 1;
724             unsigned nonprivate : 1;
725             unsigned volatil : 1;
726             unsigned isImage : 1;
727             unsigned nonUniform : 1;
728 
clearAccessChain::CoherentFlags729             void clear() {
730                 coherent = 0;
731                 devicecoherent = 0;
732                 queuefamilycoherent = 0;
733                 workgroupcoherent = 0;
734                 subgroupcoherent = 0;
735                 shadercallcoherent = 0;
736                 nonprivate = 0;
737                 volatil = 0;
738                 isImage = 0;
739                 nonUniform = 0;
740             }
741 
742             CoherentFlags operator |=(const CoherentFlags &other) {
743                 coherent |= other.coherent;
744                 devicecoherent |= other.devicecoherent;
745                 queuefamilycoherent |= other.queuefamilycoherent;
746                 workgroupcoherent |= other.workgroupcoherent;
747                 subgroupcoherent |= other.subgroupcoherent;
748                 shadercallcoherent |= other.shadercallcoherent;
749                 nonprivate |= other.nonprivate;
750                 volatil |= other.volatil;
751                 isImage |= other.isImage;
752                 nonUniform |= other.nonUniform;
753                 return *this;
754             }
755         };
756         CoherentFlags coherentFlags;
757     };
758 
759     //
760     // the SPIR-V builder maintains a single active chain that
761     // the following methods operate on
762     //
763 
764     // for external save and restore
getAccessChain()765     AccessChain getAccessChain() { return accessChain; }
setAccessChain(AccessChain newChain)766     void setAccessChain(AccessChain newChain) { accessChain = newChain; }
767 
768     // clear accessChain
769     void clearAccessChain();
770 
771     // set new base as an l-value base
setAccessChainLValue(Id lValue)772     void setAccessChainLValue(Id lValue)
773     {
774         assert(isPointer(lValue));
775         accessChain.base = lValue;
776     }
777 
778     // set new base value as an r-value
setAccessChainRValue(Id rValue)779     void setAccessChainRValue(Id rValue)
780     {
781         accessChain.isRValue = true;
782         accessChain.base = rValue;
783     }
784 
785     // push offset onto the end of the chain
accessChainPush(Id offset,AccessChain::CoherentFlags coherentFlags,unsigned int alignment)786     void accessChainPush(Id offset, AccessChain::CoherentFlags coherentFlags, unsigned int alignment)
787     {
788         accessChain.indexChain.push_back(offset);
789         accessChain.coherentFlags |= coherentFlags;
790         accessChain.alignment |= alignment;
791     }
792 
793     // push new swizzle onto the end of any existing swizzle, merging into a single swizzle
794     void accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType,
795         AccessChain::CoherentFlags coherentFlags, unsigned int alignment);
796 
797     // push a dynamic component selection onto the access chain, only applicable with a
798     // non-trivial swizzle or no swizzle
accessChainPushComponent(Id component,Id preSwizzleBaseType,AccessChain::CoherentFlags coherentFlags,unsigned int alignment)799     void accessChainPushComponent(Id component, Id preSwizzleBaseType, AccessChain::CoherentFlags coherentFlags,
800         unsigned int alignment)
801     {
802         if (accessChain.swizzle.size() != 1) {
803             accessChain.component = component;
804             if (accessChain.preSwizzleBaseType == NoType)
805                 accessChain.preSwizzleBaseType = preSwizzleBaseType;
806         }
807         accessChain.coherentFlags |= coherentFlags;
808         accessChain.alignment |= alignment;
809     }
810 
811     // use accessChain and swizzle to store value
812     void accessChainStore(Id rvalue, Decoration nonUniform,
813         spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone,
814         spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);
815 
816     // use accessChain and swizzle to load an r-value
817     Id accessChainLoad(Decoration precision, Decoration l_nonUniform, Decoration r_nonUniform, Id ResultType,
818         spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax,
819             unsigned int alignment = 0);
820 
821     // Return whether or not the access chain can be represented in SPIR-V
822     // as an l-value.
823     // E.g., a[3].yx cannot be, while a[3].y and a[3].y[x] can be.
isSpvLvalue()824     bool isSpvLvalue() const { return accessChain.swizzle.size() <= 1; }
825 
826     // get the direct pointer for an l-value
827     Id accessChainGetLValue();
828 
829     // Get the inferred SPIR-V type of the result of the current access chain,
830     // based on the type of the base and the chain of dereferences.
831     Id accessChainGetInferredType();
832 
833     // Add capabilities, extensions, remove unneeded decorations, etc.,
834     // based on the resulting SPIR-V.
835     void postProcess(bool compileOnly);
836 
837     // Prune unreachable blocks in the CFG and remove unneeded decorations.
838     void postProcessCFG();
839 
840     // Add capabilities, extensions based on instructions in the module.
841     void postProcessFeatures();
842     // Hook to visit each instruction in a block in a function
843     void postProcess(Instruction&);
844     // Hook to visit each non-32-bit sized float/int operation in a block.
845     void postProcessType(const Instruction&, spv::Id typeId);
846 
847     void dump(std::vector<unsigned int>&) const;
848 
849     void createBranch(Block* block);
850     void createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock);
851     void createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control,
852         const std::vector<unsigned int>& operands);
853 
854     // Sets to generate opcode for specialization constants.
setToSpecConstCodeGenMode()855     void setToSpecConstCodeGenMode() { generatingOpCodeForSpecConst = true; }
856     // Sets to generate opcode for non-specialization constants (normal mode).
setToNormalCodeGenMode()857     void setToNormalCodeGenMode() { generatingOpCodeForSpecConst = false; }
858     // Check if the builder is generating code for spec constants.
isInSpecConstCodeGenMode()859     bool isInSpecConstCodeGenMode() { return generatingOpCodeForSpecConst; }
860 
861  protected:
862     Id makeIntConstant(Id typeId, unsigned value, bool specConstant);
863     Id makeInt64Constant(Id typeId, unsigned long long value, bool specConstant);
864     Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value);
865     Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2);
866     Id findCompositeConstant(Op typeClass, Id typeId, const std::vector<Id>& comps);
867     Id findStructConstant(Id typeId, const std::vector<Id>& comps);
868     Id collapseAccessChain();
869     void remapDynamicSwizzle();
870     void transferAccessChainSwizzle(bool dynamic);
871     void simplifyAccessChainSwizzle();
872     void createAndSetNoPredecessorBlock(const char*);
873     void createSelectionMerge(Block* mergeBlock, unsigned int control);
874     void dumpSourceInstructions(std::vector<unsigned int>&) const;
875     void dumpSourceInstructions(const spv::Id fileId, const std::string& text, std::vector<unsigned int>&) const;
876     void dumpInstructions(std::vector<unsigned int>&, const std::vector<std::unique_ptr<Instruction> >&) const;
877     void dumpModuleProcesses(std::vector<unsigned int>&) const;
878     spv::MemoryAccessMask sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc)
879         const;
880 
881     unsigned int spvVersion;     // the version of SPIR-V to emit in the header
882     SourceLanguage sourceLang;
883     int sourceVersion;
884     spv::Id sourceFileStringId;
885     spv::Id nonSemanticShaderCompilationUnitId {0};
886     spv::Id nonSemanticShaderDebugInfo {0};
887     spv::Id debugInfoNone {0};
888     spv::Id debugExpression {0}; // Debug expression with zero operations.
889     std::string sourceText;
890     int currentLine;
891     const char* currentFile;
892     spv::Id currentFileId;
893     std::stack<spv::Id> currentDebugScopeId;
894     spv::Id lastDebugScopeId;
895     bool emitOpLines;
896     bool emitNonSemanticShaderDebugInfo;
897     bool restoreNonSemanticShaderDebugInfo;
898     bool emitNonSemanticShaderDebugSource;
899     std::set<std::string> extensions;
900     std::vector<const char*> sourceExtensions;
901     std::vector<const char*> moduleProcesses;
902     AddressingModel addressModel;
903     MemoryModel memoryModel;
904     std::set<spv::Capability> capabilities;
905     int builderNumber;
906     Module module;
907     Block* buildPoint;
908     Id uniqueId;
909     Function* entryPointFunction;
910     bool generatingOpCodeForSpecConst;
911     AccessChain accessChain;
912 
913     // special blocks of instructions for output
914     std::vector<std::unique_ptr<Instruction> > strings;
915     std::vector<std::unique_ptr<Instruction> > imports;
916     std::vector<std::unique_ptr<Instruction> > entryPoints;
917     std::vector<std::unique_ptr<Instruction> > executionModes;
918     std::vector<std::unique_ptr<Instruction> > names;
919     std::vector<std::unique_ptr<Instruction> > decorations;
920     std::vector<std::unique_ptr<Instruction> > constantsTypesGlobals;
921     std::vector<std::unique_ptr<Instruction> > externals;
922     std::vector<std::unique_ptr<Function> > functions;
923 
924     // not output, internally used for quick & dirty canonical (unique) creation
925 
926     // map type opcodes to constant inst.
927     std::unordered_map<unsigned int, std::vector<Instruction*>> groupedConstants;
928     // map struct-id to constant instructions
929     std::unordered_map<unsigned int, std::vector<Instruction*>> groupedStructConstants;
930     // map type opcodes to type instructions
931     std::unordered_map<unsigned int, std::vector<Instruction*>> groupedTypes;
932     // map type opcodes to debug type instructions
933     std::unordered_map<unsigned int, std::vector<Instruction*>> groupedDebugTypes;
934     // list of OpConstantNull instructions
935     std::vector<Instruction*> nullConstants;
936 
937     // stack of switches
938     std::stack<Block*> switchMerges;
939 
940     // Our loop stack.
941     std::stack<LoopBlocks> loops;
942 
943     // map from strings to their string ids
944     std::unordered_map<std::string, spv::Id> stringIds;
945 
946     // map from include file name ids to their contents
947     std::map<spv::Id, const std::string*> includeFiles;
948 
949     // map from core id to debug id
950     std::map <spv::Id, spv::Id> debugId;
951 
952     // map from file name string id to DebugSource id
953     std::unordered_map<spv::Id, spv::Id> debugSourceId;
954 
955     // The stream for outputting warnings and errors.
956     SpvBuildLogger* logger;
957 };  // end Builder class
958 
959 };  // end spv namespace
960 
961 #endif // SpvBuilder_H
962