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 | namespace 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 | |
68 | namespace spv { |
69 | |
70 | typedef 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 | |
79 | class Builder { |
80 | public: |
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 (Id composite, Id typeId, unsigned index); |
493 | Id (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 (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 (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* ; |
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 | |