• 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 
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 makeBoolConstant(bool b, bool specConstant = false);
297     Id makeInt8Constant(int i, bool specConstant = false)
298         { return makeIntConstant(makeIntType(8),  (unsigned)i, specConstant); }
299     Id makeUint8Constant(unsigned u, bool specConstant = false)
300         { return makeIntConstant(makeUintType(8),           u, specConstant); }
301     Id makeInt16Constant(int i, bool specConstant = false)
302         { return makeIntConstant(makeIntType(16),  (unsigned)i, specConstant); }
303     Id makeUint16Constant(unsigned u, bool specConstant = false)
304         { return makeIntConstant(makeUintType(16),           u, specConstant); }
305     Id makeIntConstant(int i, bool specConstant = false)
306         { return makeIntConstant(makeIntType(32),  (unsigned)i, specConstant); }
307     Id makeUintConstant(unsigned u, bool specConstant = false)
308         { return makeIntConstant(makeUintType(32),           u, specConstant); }
309     Id makeInt64Constant(long long i, bool specConstant = false)
310         { return makeInt64Constant(makeIntType(64),  (unsigned long long)i, specConstant); }
311     Id makeUint64Constant(unsigned long long u, bool specConstant = false)
312         { return makeInt64Constant(makeUintType(64),                     u, specConstant); }
313     Id makeFloatConstant(float f, bool specConstant = false);
314     Id makeDoubleConstant(double d, bool specConstant = false);
315     Id makeFloat16Constant(float f16, bool specConstant = false);
316     Id makeFpConstant(Id type, double d, bool specConstant = false);
317 
318     // Turn the array of constants into a proper spv constant of the requested type.
319     Id makeCompositeConstant(Id type, const std::vector<Id>& comps, bool specConst = false);
320 
321     // Methods for adding information outside the CFG.
322     Instruction* addEntryPoint(ExecutionModel, Function*, const char* name);
323     void addExecutionMode(Function*, ExecutionMode mode, int value1 = -1, int value2 = -1, int value3 = -1);
324     void addExecutionMode(Function*, ExecutionMode mode, const std::vector<unsigned>& literals);
325     void addExecutionModeId(Function*, ExecutionMode mode, const std::vector<Id>& operandIds);
326     void addName(Id, const char* name);
327     void addMemberName(Id, int member, const char* name);
328     void addDecoration(Id, Decoration, int num = -1);
329     void addDecoration(Id, Decoration, const char*);
330     void addDecoration(Id, Decoration, const std::vector<unsigned>& literals);
331     void addDecoration(Id, Decoration, const std::vector<const char*>& strings);
332     void addDecorationId(Id id, Decoration, Id idDecoration);
333     void addDecorationId(Id id, Decoration, const std::vector<Id>& operandIds);
334     void addMemberDecoration(Id, unsigned int member, Decoration, int num = -1);
335     void addMemberDecoration(Id, unsigned int member, Decoration, const char*);
336     void addMemberDecoration(Id, unsigned int member, Decoration, const std::vector<unsigned>& literals);
337     void addMemberDecoration(Id, unsigned int member, Decoration, const std::vector<const char*>& strings);
338 
339     // At the end of what block do the next create*() instructions go?
setBuildPoint(Block * bp)340     void setBuildPoint(Block* bp) { buildPoint = bp; }
getBuildPoint()341     Block* getBuildPoint() const { return buildPoint; }
342 
343     // Make the entry-point function. The returned pointer is only valid
344     // for the lifetime of this builder.
345     Function* makeEntryPoint(const char*);
346 
347     // Make a shader-style function, and create its entry block if entry is non-zero.
348     // Return the function, pass back the entry.
349     // The returned pointer is only valid for the lifetime of this builder.
350     Function* makeFunctionEntry(Decoration precision, Id returnType, const char* name,
351         const std::vector<Id>& paramTypes, const std::vector<std::vector<Decoration>>& precisions, Block **entry = 0);
352 
353     // Create a return. An 'implicit' return is one not appearing in the source
354     // code.  In the case of an implicit return, no post-return block is inserted.
355     void makeReturn(bool implicit, Id retVal = 0);
356 
357     // Generate all the code needed to finish up a function.
358     void leaveFunction();
359 
360     // Create a discard.
361     void makeDiscard();
362 
363     // Create a global or function local or IO variable.
364     Id createVariable(Decoration precision, StorageClass, Id type, const char* name = nullptr,
365         Id initializer = NoResult);
366 
367     // Create an intermediate with an undefined value.
368     Id createUndefined(Id type);
369 
370     // Store into an Id and return the l-value
371     void createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone,
372         spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);
373 
374     // Load from an Id and return it
375     Id createLoad(Id lValue, spv::Decoration precision,
376         spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone,
377         spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);
378 
379     // Create an OpAccessChain instruction
380     Id createAccessChain(StorageClass, Id base, const std::vector<Id>& offsets);
381 
382     // Create an OpArrayLength instruction
383     Id createArrayLength(Id base, unsigned int member);
384 
385     // Create an OpCooperativeMatrixLengthNV instruction
386     Id createCooperativeMatrixLength(Id type);
387 
388     // Create an OpCompositeExtract instruction
389     Id createCompositeExtract(Id composite, Id typeId, unsigned index);
390     Id createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes);
391     Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index);
392     Id createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes);
393 
394     Id createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex);
395     Id createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex);
396 
397     void createNoResultOp(Op);
398     void createNoResultOp(Op, Id operand);
399     void createNoResultOp(Op, const std::vector<Id>& operands);
400     void createNoResultOp(Op, const std::vector<IdImmediate>& operands);
401     void createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask);
402     void createMemoryBarrier(unsigned executionScope, unsigned memorySemantics);
403     Id createUnaryOp(Op, Id typeId, Id operand);
404     Id createBinOp(Op, Id typeId, Id operand1, Id operand2);
405     Id createTriOp(Op, Id typeId, Id operand1, Id operand2, Id operand3);
406     Id createOp(Op, Id typeId, const std::vector<Id>& operands);
407     Id createOp(Op, Id typeId, const std::vector<IdImmediate>& operands);
408     Id createFunctionCall(spv::Function*, const std::vector<spv::Id>&);
409     Id createSpecConstantOp(Op, Id typeId, const std::vector<spv::Id>& operands, const std::vector<unsigned>& literals);
410 
411     // Take an rvalue (source) and a set of channels to extract from it to
412     // make a new rvalue, which is returned.
413     Id createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels);
414 
415     // Take a copy of an lvalue (target) and a source of components, and set the
416     // source components into the lvalue where the 'channels' say to put them.
417     // An updated version of the target is returned.
418     // (No true lvalue or stores are used.)
419     Id createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels);
420 
421     // If both the id and precision are valid, the id
422     // gets tagged with the requested precision.
423     // The passed in id is always the returned id, to simplify use patterns.
setPrecision(Id id,Decoration precision)424     Id setPrecision(Id id, Decoration precision)
425     {
426         if (precision != NoPrecision && id != NoResult)
427             addDecoration(id, precision);
428 
429         return id;
430     }
431 
432     // Can smear a scalar to a vector for the following forms:
433     //   - promoteScalar(scalar, vector)  // smear scalar to width of vector
434     //   - promoteScalar(vector, scalar)  // smear scalar to width of vector
435     //   - promoteScalar(pointer, scalar) // smear scalar to width of what pointer points to
436     //   - promoteScalar(scalar, scalar)  // do nothing
437     // Other forms are not allowed.
438     //
439     // Generally, the type of 'scalar' does not need to be the same type as the components in 'vector'.
440     // The type of the created vector is a vector of components of the same type as the scalar.
441     //
442     // Note: One of the arguments will change, with the result coming back that way rather than
443     // through the return value.
444     void promoteScalar(Decoration precision, Id& left, Id& right);
445 
446     // Make a value by smearing the scalar to fill the type.
447     // vectorType should be the correct type for making a vector of scalarVal.
448     // (No conversions are done.)
449     Id smearScalar(Decoration precision, Id scalarVal, Id vectorType);
450 
451     // Create a call to a built-in function.
452     Id createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args);
453 
454     // List of parameters used to create a texture operation
455     struct TextureParameters {
456         Id sampler;
457         Id coords;
458         Id bias;
459         Id lod;
460         Id Dref;
461         Id offset;
462         Id offsets;
463         Id gradX;
464         Id gradY;
465         Id sample;
466         Id component;
467         Id texelOut;
468         Id lodClamp;
469         Id granularity;
470         Id coarse;
471         bool nonprivate;
472         bool volatil;
473     };
474 
475     // Select the correct texture operation based on all inputs, and emit the correct instruction
476     Id createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather,
477         bool noImplicit, const TextureParameters&, ImageOperandsMask);
478 
479     // Emit the OpTextureQuery* instruction that was passed in.
480     // Figure out the right return value and type, and return it.
481     Id createTextureQueryCall(Op, const TextureParameters&, bool isUnsignedResult);
482 
483     Id createSamplePositionCall(Decoration precision, Id, Id);
484 
485     Id createBitFieldExtractCall(Decoration precision, Id, Id, Id, bool isSigned);
486     Id createBitFieldInsertCall(Decoration precision, Id, Id, Id, Id);
487 
488     // Reduction comparison for composites:  For equal and not-equal resulting in a scalar.
489     Id createCompositeCompare(Decoration precision, Id, Id, bool /* true if for equal, false if for not-equal */);
490 
491     // OpCompositeConstruct
492     Id createCompositeConstruct(Id typeId, const std::vector<Id>& constituents);
493 
494     // vector or scalar constructor
495     Id createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId);
496 
497     // matrix constructor
498     Id createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id constructee);
499 
500     // Helper to use for building nested control flow with if-then-else.
501     class If {
502     public:
503         If(Id condition, unsigned int ctrl, Builder& builder);
~If()504         ~If() {}
505 
506         void makeBeginElse();
507         void makeEndIf();
508 
509     private:
510         If(const If&);
511         If& operator=(If&);
512 
513         Builder& builder;
514         Id condition;
515         unsigned int control;
516         Function* function;
517         Block* headerBlock;
518         Block* thenBlock;
519         Block* elseBlock;
520         Block* mergeBlock;
521     };
522 
523     // Make a switch statement.  A switch has 'numSegments' of pieces of code, not containing
524     // any case/default labels, all separated by one or more case/default labels.  Each possible
525     // case value v is a jump to the caseValues[v] segment.  The defaultSegment is also in this
526     // number space.  How to compute the value is given by 'condition', as in switch(condition).
527     //
528     // The SPIR-V Builder will maintain the stack of post-switch merge blocks for nested switches.
529     //
530     // Use a defaultSegment < 0 if there is no default segment (to branch to post switch).
531     //
532     // Returns the right set of basic blocks to start each code segment with, so that the caller's
533     // recursion stack can hold the memory for it.
534     //
535     void makeSwitch(Id condition, unsigned int control, int numSegments, const std::vector<int>& caseValues,
536                     const std::vector<int>& valueToSegment, int defaultSegment, std::vector<Block*>& segmentBB);
537 
538     // Add a branch to the innermost switch's merge block.
539     void addSwitchBreak();
540 
541     // Move to the next code segment, passing in the return argument in makeSwitch()
542     void nextSwitchSegment(std::vector<Block*>& segmentBB, int segment);
543 
544     // Finish off the innermost switch.
545     void endSwitch(std::vector<Block*>& segmentBB);
546 
547     struct LoopBlocks {
LoopBlocksLoopBlocks548         LoopBlocks(Block& head, Block& body, Block& merge, Block& continue_target) :
549             head(head), body(body), merge(merge), continue_target(continue_target) { }
550         Block &head, &body, &merge, &continue_target;
551     private:
552         LoopBlocks();
553         LoopBlocks& operator=(const LoopBlocks&) = delete;
554     };
555 
556     // Start a new loop and prepare the builder to generate code for it.  Until
557     // closeLoop() is called for this loop, createLoopContinue() and
558     // createLoopExit() will target its corresponding blocks.
559     LoopBlocks& makeNewLoop();
560 
561     // Create a new block in the function containing the build point.  Memory is
562     // owned by the function object.
563     Block& makeNewBlock();
564 
565     // Add a branch to the continue_target of the current (innermost) loop.
566     void createLoopContinue();
567 
568     // Add an exit (e.g. "break") from the innermost loop that we're currently
569     // in.
570     void createLoopExit();
571 
572     // Close the innermost loop that you're in
573     void closeLoop();
574 
575     //
576     // Access chain design for an R-Value vs. L-Value:
577     //
578     // There is a single access chain the builder is building at
579     // any particular time.  Such a chain can be used to either to a load or
580     // a store, when desired.
581     //
582     // Expressions can be r-values, l-values, or both, or only r-values:
583     //    a[b.c].d = ....  // l-value
584     //    ... = a[b.c].d;  // r-value, that also looks like an l-value
585     //    ++a[b.c].d;      // r-value and l-value
586     //    (x + y)[2];      // r-value only, can't possibly be l-value
587     //
588     // Computing an r-value means generating code.  Hence,
589     // r-values should only be computed when they are needed, not speculatively.
590     //
591     // Computing an l-value means saving away information for later use in the compiler,
592     // no code is generated until the l-value is later dereferenced.  It is okay
593     // to speculatively generate an l-value, just not okay to speculatively dereference it.
594     //
595     // The base of the access chain (the left-most variable or expression
596     // from which everything is based) can be set either as an l-value
597     // or as an r-value.  Most efficient would be to set an l-value if one
598     // is available.  If an expression was evaluated, the resulting r-value
599     // can be set as the chain base.
600     //
601     // The users of this single access chain can save and restore if they
602     // want to nest or manage multiple chains.
603     //
604 
605     struct AccessChain {
606         Id base;                       // for l-values, pointer to the base object, for r-values, the base object
607         std::vector<Id> indexChain;
608         Id instr;                      // cache the instruction that generates this access chain
609         std::vector<unsigned> swizzle; // each std::vector element selects the next GLSL component number
610         Id component;                  // a dynamic component index, can coexist with a swizzle,
611                                        // done after the swizzle, NoResult if not present
612         Id preSwizzleBaseType;         // dereferenced type, before swizzle or component is applied;
613                                        // NoType unless a swizzle or component is present
614         bool isRValue;                 // true if 'base' is an r-value, otherwise, base is an l-value
615         unsigned int alignment;        // bitwise OR of alignment values passed in. Accumulates worst alignment.
616                                        // Only tracks base and (optional) component selection alignment.
617 
618         // Accumulate whether anything in the chain of structures has coherent decorations.
619         struct CoherentFlags {
CoherentFlagsAccessChain::CoherentFlags620             CoherentFlags() { clear(); }
621 #ifdef GLSLANG_WEB
clearAccessChain::CoherentFlags622             void clear() { }
isVolatileAccessChain::CoherentFlags623             bool isVolatile() const { return false; }
624             CoherentFlags operator |=(const CoherentFlags &other) { return *this; }
625 #else
isVolatileAccessChain::CoherentFlags626             bool isVolatile() const { return volatil; }
anyCoherentAccessChain::CoherentFlags627             bool anyCoherent() const {
628                 return coherent || devicecoherent || queuefamilycoherent || workgroupcoherent ||
629                     subgroupcoherent || shadercallcoherent;
630             }
631 
632             unsigned coherent : 1;
633             unsigned devicecoherent : 1;
634             unsigned queuefamilycoherent : 1;
635             unsigned workgroupcoherent : 1;
636             unsigned subgroupcoherent : 1;
637             unsigned shadercallcoherent : 1;
638             unsigned nonprivate : 1;
639             unsigned volatil : 1;
640             unsigned isImage : 1;
641 
clearAccessChain::CoherentFlags642             void clear() {
643                 coherent = 0;
644                 devicecoherent = 0;
645                 queuefamilycoherent = 0;
646                 workgroupcoherent = 0;
647                 subgroupcoherent = 0;
648                 shadercallcoherent = 0;
649                 nonprivate = 0;
650                 volatil = 0;
651                 isImage = 0;
652             }
653 
654             CoherentFlags operator |=(const CoherentFlags &other) {
655                 coherent |= other.coherent;
656                 devicecoherent |= other.devicecoherent;
657                 queuefamilycoherent |= other.queuefamilycoherent;
658                 workgroupcoherent |= other.workgroupcoherent;
659                 subgroupcoherent |= other.subgroupcoherent;
660                 shadercallcoherent |= other.shadercallcoherent;
661                 nonprivate |= other.nonprivate;
662                 volatil |= other.volatil;
663                 isImage |= other.isImage;
664                 return *this;
665             }
666 #endif
667         };
668         CoherentFlags coherentFlags;
669     };
670 
671     //
672     // the SPIR-V builder maintains a single active chain that
673     // the following methods operate on
674     //
675 
676     // for external save and restore
getAccessChain()677     AccessChain getAccessChain() { return accessChain; }
setAccessChain(AccessChain newChain)678     void setAccessChain(AccessChain newChain) { accessChain = newChain; }
679 
680     // clear accessChain
681     void clearAccessChain();
682 
683     // set new base as an l-value base
setAccessChainLValue(Id lValue)684     void setAccessChainLValue(Id lValue)
685     {
686         assert(isPointer(lValue));
687         accessChain.base = lValue;
688     }
689 
690     // set new base value as an r-value
setAccessChainRValue(Id rValue)691     void setAccessChainRValue(Id rValue)
692     {
693         accessChain.isRValue = true;
694         accessChain.base = rValue;
695     }
696 
697     // push offset onto the end of the chain
accessChainPush(Id offset,AccessChain::CoherentFlags coherentFlags,unsigned int alignment)698     void accessChainPush(Id offset, AccessChain::CoherentFlags coherentFlags, unsigned int alignment)
699     {
700         accessChain.indexChain.push_back(offset);
701         accessChain.coherentFlags |= coherentFlags;
702         accessChain.alignment |= alignment;
703     }
704 
705     // push new swizzle onto the end of any existing swizzle, merging into a single swizzle
706     void accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType,
707         AccessChain::CoherentFlags coherentFlags, unsigned int alignment);
708 
709     // push a dynamic component selection onto the access chain, only applicable with a
710     // non-trivial swizzle or no swizzle
accessChainPushComponent(Id component,Id preSwizzleBaseType,AccessChain::CoherentFlags coherentFlags,unsigned int alignment)711     void accessChainPushComponent(Id component, Id preSwizzleBaseType, AccessChain::CoherentFlags coherentFlags,
712         unsigned int alignment)
713     {
714         if (accessChain.swizzle.size() != 1) {
715             accessChain.component = component;
716             if (accessChain.preSwizzleBaseType == NoType)
717                 accessChain.preSwizzleBaseType = preSwizzleBaseType;
718         }
719         accessChain.coherentFlags |= coherentFlags;
720         accessChain.alignment |= alignment;
721     }
722 
723     // use accessChain and swizzle to store value
724     void accessChainStore(Id rvalue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone,
725         spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);
726 
727     // use accessChain and swizzle to load an r-value
728     Id accessChainLoad(Decoration precision, Decoration nonUniform, Id ResultType,
729         spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax,
730             unsigned int alignment = 0);
731 
732     // Return whether or not the access chain can be represented in SPIR-V
733     // as an l-value.
734     // E.g., a[3].yx cannot be, while a[3].y and a[3].y[x] can be.
isSpvLvalue()735     bool isSpvLvalue() const { return accessChain.swizzle.size() <= 1; }
736 
737     // get the direct pointer for an l-value
738     Id accessChainGetLValue();
739 
740     // Get the inferred SPIR-V type of the result of the current access chain,
741     // based on the type of the base and the chain of dereferences.
742     Id accessChainGetInferredType();
743 
744     // Add capabilities, extensions, remove unneeded decorations, etc.,
745     // based on the resulting SPIR-V.
746     void postProcess();
747 
748     // Prune unreachable blocks in the CFG and remove unneeded decorations.
749     void postProcessCFG();
750 
751 #ifndef GLSLANG_WEB
752     // Add capabilities, extensions based on instructions in the module.
753     void postProcessFeatures();
754     // Hook to visit each instruction in a block in a function
755     void postProcess(Instruction&);
756     // Hook to visit each non-32-bit sized float/int operation in a block.
757     void postProcessType(const Instruction&, spv::Id typeId);
758 #endif
759 
760     void dump(std::vector<unsigned int>&) const;
761 
762     void createBranch(Block* block);
763     void createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock);
764     void createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control,
765         const std::vector<unsigned int>& operands);
766 
767     // Sets to generate opcode for specialization constants.
setToSpecConstCodeGenMode()768     void setToSpecConstCodeGenMode() { generatingOpCodeForSpecConst = true; }
769     // Sets to generate opcode for non-specialization constants (normal mode).
setToNormalCodeGenMode()770     void setToNormalCodeGenMode() { generatingOpCodeForSpecConst = false; }
771     // Check if the builder is generating code for spec constants.
isInSpecConstCodeGenMode()772     bool isInSpecConstCodeGenMode() { return generatingOpCodeForSpecConst; }
773 
774  protected:
775     Id makeIntConstant(Id typeId, unsigned value, bool specConstant);
776     Id makeInt64Constant(Id typeId, unsigned long long value, bool specConstant);
777     Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value);
778     Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2);
779     Id findCompositeConstant(Op typeClass, Id typeId, const std::vector<Id>& comps);
780     Id findStructConstant(Id typeId, const std::vector<Id>& comps);
781     Id collapseAccessChain();
782     void remapDynamicSwizzle();
783     void transferAccessChainSwizzle(bool dynamic);
784     void simplifyAccessChainSwizzle();
785     void createAndSetNoPredecessorBlock(const char*);
786     void createSelectionMerge(Block* mergeBlock, unsigned int control);
787     void dumpSourceInstructions(std::vector<unsigned int>&) const;
788     void dumpSourceInstructions(const spv::Id fileId, const std::string& text, std::vector<unsigned int>&) const;
789     void dumpInstructions(std::vector<unsigned int>&, const std::vector<std::unique_ptr<Instruction> >&) const;
790     void dumpModuleProcesses(std::vector<unsigned int>&) const;
791     spv::MemoryAccessMask sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc)
792         const;
793 
794     unsigned int spvVersion;     // the version of SPIR-V to emit in the header
795     SourceLanguage source;
796     int sourceVersion;
797     spv::Id sourceFileStringId;
798     std::string sourceText;
799     int currentLine;
800     const char* currentFile;
801     bool emitOpLines;
802     std::set<std::string> extensions;
803     std::vector<const char*> sourceExtensions;
804     std::vector<const char*> moduleProcesses;
805     AddressingModel addressModel;
806     MemoryModel memoryModel;
807     std::set<spv::Capability> capabilities;
808     int builderNumber;
809     Module module;
810     Block* buildPoint;
811     Id uniqueId;
812     Function* entryPointFunction;
813     bool generatingOpCodeForSpecConst;
814     AccessChain accessChain;
815 
816     // special blocks of instructions for output
817     std::vector<std::unique_ptr<Instruction> > strings;
818     std::vector<std::unique_ptr<Instruction> > imports;
819     std::vector<std::unique_ptr<Instruction> > entryPoints;
820     std::vector<std::unique_ptr<Instruction> > executionModes;
821     std::vector<std::unique_ptr<Instruction> > names;
822     std::vector<std::unique_ptr<Instruction> > decorations;
823     std::vector<std::unique_ptr<Instruction> > constantsTypesGlobals;
824     std::vector<std::unique_ptr<Instruction> > externals;
825     std::vector<std::unique_ptr<Function> > functions;
826 
827     // not output, internally used for quick & dirty canonical (unique) creation
828 
829     // map type opcodes to constant inst.
830     std::unordered_map<unsigned int, std::vector<Instruction*>> groupedConstants;
831     // map struct-id to constant instructions
832     std::unordered_map<unsigned int, std::vector<Instruction*>> groupedStructConstants;
833     // map type opcodes to type instructions
834     std::unordered_map<unsigned int, std::vector<Instruction*>> groupedTypes;
835 
836     // stack of switches
837     std::stack<Block*> switchMerges;
838 
839     // Our loop stack.
840     std::stack<LoopBlocks> loops;
841 
842     // map from strings to their string ids
843     std::unordered_map<std::string, spv::Id> stringIds;
844 
845     // map from include file name ids to their contents
846     std::map<spv::Id, const std::string*> includeFiles;
847 
848     // The stream for outputting warnings and errors.
849     SpvBuildLogger* logger;
850 };  // end Builder class
851 
852 };  // end spv namespace
853 
854 #endif // SpvBuilder_H
855