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