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