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

source code of qtshadertools/src/3rdparty/glslang/SPIRV/SpvBuilder.h