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

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