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