1 | // |
2 | // Copyright (C) 2014-2015 LunarG, Inc. |
3 | // Copyright (C) 2015-2018 Google, Inc. |
4 | // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. |
5 | // |
6 | // All rights reserved. |
7 | // |
8 | // Redistribution and use in source and binary forms, with or without |
9 | // modification, are permitted provided that the following conditions |
10 | // are met: |
11 | // |
12 | // Redistributions of source code must retain the above copyright |
13 | // notice, this list of conditions and the following disclaimer. |
14 | // |
15 | // Redistributions in binary form must reproduce the above |
16 | // copyright notice, this list of conditions and the following |
17 | // disclaimer in the documentation and/or other materials provided |
18 | // with the distribution. |
19 | // |
20 | // Neither the name of 3Dlabs Inc. Ltd. nor the names of its |
21 | // contributors may be used to endorse or promote products derived |
22 | // from this software without specific prior written permission. |
23 | // |
24 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
25 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
26 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
27 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
28 | // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
29 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
30 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
31 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
32 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
33 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
34 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
35 | // POSSIBILITY OF SUCH DAMAGE. |
36 | |
37 | // |
38 | // Helper for making SPIR-V IR. Generally, this is documented in the header |
39 | // SpvBuilder.h. |
40 | // |
41 | |
42 | #include <cassert> |
43 | #include <cstdlib> |
44 | |
45 | #include <unordered_set> |
46 | #include <algorithm> |
47 | |
48 | #include "SpvBuilder.h" |
49 | |
50 | #ifndef GLSLANG_WEB |
51 | #include "hex_float.h" |
52 | #endif |
53 | |
54 | #ifndef _WIN32 |
55 | #include <cstdio> |
56 | #endif |
57 | |
58 | namespace spv { |
59 | |
60 | Builder::Builder(unsigned int spvVersion, unsigned int magicNumber, SpvBuildLogger* buildLogger) : |
61 | spvVersion(spvVersion), |
62 | sourceLang(SourceLanguageUnknown), |
63 | sourceVersion(0), |
64 | sourceFileStringId(NoResult), |
65 | currentLine(0), |
66 | currentFile(nullptr), |
67 | currentFileId(NoResult), |
68 | lastDebugScopeId(NoResult), |
69 | emitOpLines(false), |
70 | emitNonSemanticShaderDebugInfo(false), |
71 | addressModel(AddressingModelLogical), |
72 | memoryModel(MemoryModelGLSL450), |
73 | builderNumber(magicNumber), |
74 | buildPoint(nullptr), |
75 | uniqueId(0), |
76 | entryPointFunction(nullptr), |
77 | generatingOpCodeForSpecConst(false), |
78 | logger(buildLogger) |
79 | { |
80 | clearAccessChain(); |
81 | } |
82 | |
83 | Builder::~Builder() |
84 | { |
85 | } |
86 | |
87 | Id Builder::import(const char* name) |
88 | { |
89 | Instruction* import = new Instruction(getUniqueId(), NoType, OpExtInstImport); |
90 | import->addStringOperand(name); |
91 | module.mapInstruction(import); |
92 | |
93 | imports.push_back(std::unique_ptr<Instruction>(import)); |
94 | return import->getResultId(); |
95 | } |
96 | |
97 | // Emit instruction for non-filename-based #line directives (ie. no filename |
98 | // seen yet): emit an OpLine if we've been asked to emit OpLines and the line |
99 | // number has changed since the last time, and is a valid line number. |
100 | void Builder::setLine(int lineNum) |
101 | { |
102 | if (lineNum != 0 && lineNum != currentLine) { |
103 | currentLine = lineNum; |
104 | if (emitOpLines) { |
105 | if (emitNonSemanticShaderDebugInfo) |
106 | addDebugScopeAndLine(currentFileId, currentLine, 0); |
107 | else |
108 | addLine(sourceFileStringId, currentLine, 0); |
109 | } |
110 | } |
111 | } |
112 | |
113 | // If no filename, do non-filename-based #line emit. Else do filename-based emit. |
114 | // Emit OpLine if we've been asked to emit OpLines and the line number or filename |
115 | // has changed since the last time, and line number is valid. |
116 | void Builder::setLine(int lineNum, const char* filename) |
117 | { |
118 | if (filename == nullptr) { |
119 | setLine(lineNum); |
120 | return; |
121 | } |
122 | if ((lineNum != 0 && lineNum != currentLine) || currentFile == nullptr || |
123 | strncmp(filename, currentFile, strlen(currentFile) + 1) != 0) { |
124 | currentLine = lineNum; |
125 | currentFile = filename; |
126 | if (emitOpLines) { |
127 | spv::Id strId = getStringId(filename); |
128 | if (emitNonSemanticShaderDebugInfo) |
129 | addDebugScopeAndLine(strId, currentLine, 0); |
130 | else |
131 | addLine(strId, currentLine, 0); |
132 | } |
133 | } |
134 | } |
135 | |
136 | void Builder::addLine(Id fileName, int lineNum, int column) |
137 | { |
138 | Instruction* line = new Instruction(OpLine); |
139 | line->addIdOperand(fileName); |
140 | line->addImmediateOperand(lineNum); |
141 | line->addImmediateOperand(column); |
142 | buildPoint->addInstruction(std::unique_ptr<Instruction>(line)); |
143 | } |
144 | |
145 | void Builder::addDebugScopeAndLine(Id fileName, int lineNum, int column) |
146 | { |
147 | assert(!currentDebugScopeId.empty()); |
148 | if (currentDebugScopeId.top() != lastDebugScopeId) { |
149 | spv::Id resultId = getUniqueId(); |
150 | Instruction* scopeInst = new Instruction(resultId, makeVoidType(), OpExtInst); |
151 | scopeInst->addIdOperand(nonSemanticShaderDebugInfo); |
152 | scopeInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugScope); |
153 | scopeInst->addIdOperand(currentDebugScopeId.top()); |
154 | buildPoint->addInstruction(std::unique_ptr<Instruction>(scopeInst)); |
155 | lastDebugScopeId = currentDebugScopeId.top(); |
156 | } |
157 | spv::Id resultId = getUniqueId(); |
158 | Instruction* lineInst = new Instruction(resultId, makeVoidType(), OpExtInst); |
159 | lineInst->addIdOperand(nonSemanticShaderDebugInfo); |
160 | lineInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugLine); |
161 | lineInst->addIdOperand(makeDebugSource(fileName)); |
162 | lineInst->addIdOperand(makeUintConstant(lineNum)); |
163 | lineInst->addIdOperand(makeUintConstant(lineNum)); |
164 | lineInst->addIdOperand(makeUintConstant(column)); |
165 | lineInst->addIdOperand(makeUintConstant(column)); |
166 | buildPoint->addInstruction(std::unique_ptr<Instruction>(lineInst)); |
167 | } |
168 | |
169 | // For creating new groupedTypes (will return old type if the requested one was already made). |
170 | Id Builder::makeVoidType() |
171 | { |
172 | Instruction* type; |
173 | if (groupedTypes[OpTypeVoid].size() == 0) { |
174 | Id typeId = getUniqueId(); |
175 | type = new Instruction(typeId, NoType, OpTypeVoid); |
176 | groupedTypes[OpTypeVoid].push_back(type); |
177 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
178 | module.mapInstruction(type); |
179 | // Core OpTypeVoid used for debug void type |
180 | if (emitNonSemanticShaderDebugInfo) |
181 | debugId[typeId] = typeId; |
182 | } else |
183 | type = groupedTypes[OpTypeVoid].back(); |
184 | |
185 | return type->getResultId(); |
186 | } |
187 | |
188 | Id Builder::makeBoolType(bool const compilerGenerated) |
189 | { |
190 | Instruction* type; |
191 | if (groupedTypes[OpTypeBool].size() == 0) { |
192 | type = new Instruction(getUniqueId(), NoType, OpTypeBool); |
193 | groupedTypes[OpTypeBool].push_back(type); |
194 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
195 | module.mapInstruction(type); |
196 | } else |
197 | type = groupedTypes[OpTypeBool].back(); |
198 | |
199 | if (emitNonSemanticShaderDebugInfo && !compilerGenerated) |
200 | { |
201 | auto const debugResultId = makeBoolDebugType(32); |
202 | debugId[type->getResultId()] = debugResultId; |
203 | } |
204 | |
205 | return type->getResultId(); |
206 | } |
207 | |
208 | Id Builder::makeSamplerType() |
209 | { |
210 | Instruction* type; |
211 | if (groupedTypes[OpTypeSampler].size() == 0) { |
212 | type = new Instruction(getUniqueId(), NoType, OpTypeSampler); |
213 | groupedTypes[OpTypeSampler].push_back(type); |
214 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
215 | module.mapInstruction(type); |
216 | } else |
217 | type = groupedTypes[OpTypeSampler].back(); |
218 | |
219 | if (emitNonSemanticShaderDebugInfo) |
220 | { |
221 | auto const debugResultId = makeCompositeDebugType({}, "type.sampler" , NonSemanticShaderDebugInfo100Structure, true); |
222 | debugId[type->getResultId()] = debugResultId; |
223 | } |
224 | |
225 | return type->getResultId(); |
226 | } |
227 | |
228 | Id Builder::makePointer(StorageClass storageClass, Id pointee) |
229 | { |
230 | // try to find it |
231 | Instruction* type; |
232 | for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) { |
233 | type = groupedTypes[OpTypePointer][t]; |
234 | if (type->getImmediateOperand(0) == (unsigned)storageClass && |
235 | type->getIdOperand(1) == pointee) |
236 | return type->getResultId(); |
237 | } |
238 | |
239 | // not found, make it |
240 | type = new Instruction(getUniqueId(), NoType, OpTypePointer); |
241 | type->addImmediateOperand(storageClass); |
242 | type->addIdOperand(pointee); |
243 | groupedTypes[OpTypePointer].push_back(type); |
244 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
245 | module.mapInstruction(type); |
246 | |
247 | return type->getResultId(); |
248 | } |
249 | |
250 | Id Builder::makeForwardPointer(StorageClass storageClass) |
251 | { |
252 | // Caching/uniquifying doesn't work here, because we don't know the |
253 | // pointee type and there can be multiple forward pointers of the same |
254 | // storage type. Somebody higher up in the stack must keep track. |
255 | Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeForwardPointer); |
256 | type->addImmediateOperand(storageClass); |
257 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
258 | module.mapInstruction(type); |
259 | |
260 | return type->getResultId(); |
261 | } |
262 | |
263 | Id Builder::makePointerFromForwardPointer(StorageClass storageClass, Id forwardPointerType, Id pointee) |
264 | { |
265 | // try to find it |
266 | Instruction* type; |
267 | for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) { |
268 | type = groupedTypes[OpTypePointer][t]; |
269 | if (type->getImmediateOperand(0) == (unsigned)storageClass && |
270 | type->getIdOperand(1) == pointee) |
271 | return type->getResultId(); |
272 | } |
273 | |
274 | type = new Instruction(forwardPointerType, NoType, OpTypePointer); |
275 | type->addImmediateOperand(storageClass); |
276 | type->addIdOperand(pointee); |
277 | groupedTypes[OpTypePointer].push_back(type); |
278 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
279 | module.mapInstruction(type); |
280 | |
281 | return type->getResultId(); |
282 | } |
283 | |
284 | Id Builder::makeIntegerType(int width, bool hasSign) |
285 | { |
286 | #ifdef GLSLANG_WEB |
287 | assert(width == 32); |
288 | width = 32; |
289 | #endif |
290 | |
291 | // try to find it |
292 | Instruction* type; |
293 | for (int t = 0; t < (int)groupedTypes[OpTypeInt].size(); ++t) { |
294 | type = groupedTypes[OpTypeInt][t]; |
295 | if (type->getImmediateOperand(0) == (unsigned)width && |
296 | type->getImmediateOperand(1) == (hasSign ? 1u : 0u)) |
297 | return type->getResultId(); |
298 | } |
299 | |
300 | // not found, make it |
301 | type = new Instruction(getUniqueId(), NoType, OpTypeInt); |
302 | type->addImmediateOperand(width); |
303 | type->addImmediateOperand(hasSign ? 1 : 0); |
304 | groupedTypes[OpTypeInt].push_back(type); |
305 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
306 | module.mapInstruction(type); |
307 | |
308 | // deal with capabilities |
309 | switch (width) { |
310 | case 8: |
311 | case 16: |
312 | // these are currently handled by storage-type declarations and post processing |
313 | break; |
314 | case 64: |
315 | addCapability(CapabilityInt64); |
316 | break; |
317 | default: |
318 | break; |
319 | } |
320 | |
321 | if (emitNonSemanticShaderDebugInfo) |
322 | { |
323 | auto const debugResultId = makeIntegerDebugType(width, hasSign); |
324 | debugId[type->getResultId()] = debugResultId; |
325 | } |
326 | |
327 | return type->getResultId(); |
328 | } |
329 | |
330 | Id Builder::makeFloatType(int width) |
331 | { |
332 | #ifdef GLSLANG_WEB |
333 | assert(width == 32); |
334 | width = 32; |
335 | #endif |
336 | |
337 | // try to find it |
338 | Instruction* type; |
339 | for (int t = 0; t < (int)groupedTypes[OpTypeFloat].size(); ++t) { |
340 | type = groupedTypes[OpTypeFloat][t]; |
341 | if (type->getImmediateOperand(0) == (unsigned)width) |
342 | return type->getResultId(); |
343 | } |
344 | |
345 | // not found, make it |
346 | type = new Instruction(getUniqueId(), NoType, OpTypeFloat); |
347 | type->addImmediateOperand(width); |
348 | groupedTypes[OpTypeFloat].push_back(type); |
349 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
350 | module.mapInstruction(type); |
351 | |
352 | // deal with capabilities |
353 | switch (width) { |
354 | case 16: |
355 | // currently handled by storage-type declarations and post processing |
356 | break; |
357 | case 64: |
358 | addCapability(CapabilityFloat64); |
359 | break; |
360 | default: |
361 | break; |
362 | } |
363 | |
364 | if (emitNonSemanticShaderDebugInfo) |
365 | { |
366 | auto const debugResultId = makeFloatDebugType(width); |
367 | debugId[type->getResultId()] = debugResultId; |
368 | } |
369 | |
370 | return type->getResultId(); |
371 | } |
372 | |
373 | // Make a struct without checking for duplication. |
374 | // See makeStructResultType() for non-decorated structs |
375 | // needed as the result of some instructions, which does |
376 | // check for duplicates. |
377 | Id Builder::makeStructType(const std::vector<Id>& members, const char* name, bool const compilerGenerated) |
378 | { |
379 | // Don't look for previous one, because in the general case, |
380 | // structs can be duplicated except for decorations. |
381 | |
382 | // not found, make it |
383 | Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeStruct); |
384 | for (int op = 0; op < (int)members.size(); ++op) |
385 | type->addIdOperand(members[op]); |
386 | groupedTypes[OpTypeStruct].push_back(type); |
387 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
388 | module.mapInstruction(type); |
389 | addName(type->getResultId(), name); |
390 | |
391 | if (emitNonSemanticShaderDebugInfo && !compilerGenerated) |
392 | { |
393 | auto const debugResultId = makeCompositeDebugType(members, name, NonSemanticShaderDebugInfo100Structure); |
394 | debugId[type->getResultId()] = debugResultId; |
395 | } |
396 | |
397 | return type->getResultId(); |
398 | } |
399 | |
400 | // Make a struct for the simple results of several instructions, |
401 | // checking for duplication. |
402 | Id Builder::makeStructResultType(Id type0, Id type1) |
403 | { |
404 | // try to find it |
405 | Instruction* type; |
406 | for (int t = 0; t < (int)groupedTypes[OpTypeStruct].size(); ++t) { |
407 | type = groupedTypes[OpTypeStruct][t]; |
408 | if (type->getNumOperands() != 2) |
409 | continue; |
410 | if (type->getIdOperand(0) != type0 || |
411 | type->getIdOperand(1) != type1) |
412 | continue; |
413 | return type->getResultId(); |
414 | } |
415 | |
416 | // not found, make it |
417 | std::vector<spv::Id> members; |
418 | members.push_back(type0); |
419 | members.push_back(type1); |
420 | |
421 | return makeStructType(members, "ResType" ); |
422 | } |
423 | |
424 | Id Builder::makeVectorType(Id component, int size) |
425 | { |
426 | // try to find it |
427 | Instruction* type; |
428 | for (int t = 0; t < (int)groupedTypes[OpTypeVector].size(); ++t) { |
429 | type = groupedTypes[OpTypeVector][t]; |
430 | if (type->getIdOperand(0) == component && |
431 | type->getImmediateOperand(1) == (unsigned)size) |
432 | return type->getResultId(); |
433 | } |
434 | |
435 | // not found, make it |
436 | type = new Instruction(getUniqueId(), NoType, OpTypeVector); |
437 | type->addIdOperand(component); |
438 | type->addImmediateOperand(size); |
439 | groupedTypes[OpTypeVector].push_back(type); |
440 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
441 | module.mapInstruction(type); |
442 | |
443 | if (emitNonSemanticShaderDebugInfo) |
444 | { |
445 | auto const debugResultId = makeVectorDebugType(component, size); |
446 | debugId[type->getResultId()] = debugResultId; |
447 | } |
448 | |
449 | return type->getResultId(); |
450 | } |
451 | |
452 | Id Builder::makeMatrixType(Id component, int cols, int rows) |
453 | { |
454 | assert(cols <= maxMatrixSize && rows <= maxMatrixSize); |
455 | |
456 | Id column = makeVectorType(component, rows); |
457 | |
458 | // try to find it |
459 | Instruction* type; |
460 | for (int t = 0; t < (int)groupedTypes[OpTypeMatrix].size(); ++t) { |
461 | type = groupedTypes[OpTypeMatrix][t]; |
462 | if (type->getIdOperand(0) == column && |
463 | type->getImmediateOperand(1) == (unsigned)cols) |
464 | return type->getResultId(); |
465 | } |
466 | |
467 | // not found, make it |
468 | type = new Instruction(getUniqueId(), NoType, OpTypeMatrix); |
469 | type->addIdOperand(column); |
470 | type->addImmediateOperand(cols); |
471 | groupedTypes[OpTypeMatrix].push_back(type); |
472 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
473 | module.mapInstruction(type); |
474 | |
475 | if (emitNonSemanticShaderDebugInfo) |
476 | { |
477 | auto const debugResultId = makeMatrixDebugType(column, cols); |
478 | debugId[type->getResultId()] = debugResultId; |
479 | } |
480 | |
481 | return type->getResultId(); |
482 | } |
483 | |
484 | Id Builder::makeCooperativeMatrixType(Id component, Id scope, Id rows, Id cols) |
485 | { |
486 | // try to find it |
487 | Instruction* type; |
488 | for (int t = 0; t < (int)groupedTypes[OpTypeCooperativeMatrixNV].size(); ++t) { |
489 | type = groupedTypes[OpTypeCooperativeMatrixNV][t]; |
490 | if (type->getIdOperand(0) == component && |
491 | type->getIdOperand(1) == scope && |
492 | type->getIdOperand(2) == rows && |
493 | type->getIdOperand(3) == cols) |
494 | return type->getResultId(); |
495 | } |
496 | |
497 | // not found, make it |
498 | type = new Instruction(getUniqueId(), NoType, OpTypeCooperativeMatrixNV); |
499 | type->addIdOperand(component); |
500 | type->addIdOperand(scope); |
501 | type->addIdOperand(rows); |
502 | type->addIdOperand(cols); |
503 | groupedTypes[OpTypeCooperativeMatrixNV].push_back(type); |
504 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
505 | module.mapInstruction(type); |
506 | |
507 | return type->getResultId(); |
508 | } |
509 | |
510 | Id Builder::makeGenericType(spv::Op opcode, std::vector<spv::IdImmediate>& operands) |
511 | { |
512 | // try to find it |
513 | Instruction* type; |
514 | for (int t = 0; t < (int)groupedTypes[opcode].size(); ++t) { |
515 | type = groupedTypes[opcode][t]; |
516 | if (static_cast<size_t>(type->getNumOperands()) != operands.size()) |
517 | continue; // Number mismatch, find next |
518 | |
519 | bool match = true; |
520 | for (int op = 0; match && op < (int)operands.size(); ++op) { |
521 | match = (operands[op].isId ? type->getIdOperand(op) : type->getImmediateOperand(op)) == operands[op].word; |
522 | } |
523 | if (match) |
524 | return type->getResultId(); |
525 | } |
526 | |
527 | // not found, make it |
528 | type = new Instruction(getUniqueId(), NoType, opcode); |
529 | for (size_t op = 0; op < operands.size(); ++op) { |
530 | if (operands[op].isId) |
531 | type->addIdOperand(operands[op].word); |
532 | else |
533 | type->addImmediateOperand(operands[op].word); |
534 | } |
535 | groupedTypes[opcode].push_back(type); |
536 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
537 | module.mapInstruction(type); |
538 | |
539 | return type->getResultId(); |
540 | } |
541 | |
542 | // TODO: performance: track arrays per stride |
543 | // If a stride is supplied (non-zero) make an array. |
544 | // If no stride (0), reuse previous array types. |
545 | // 'size' is an Id of a constant or specialization constant of the array size |
546 | Id Builder::makeArrayType(Id element, Id sizeId, int stride) |
547 | { |
548 | Instruction* type; |
549 | if (stride == 0) { |
550 | // try to find existing type |
551 | for (int t = 0; t < (int)groupedTypes[OpTypeArray].size(); ++t) { |
552 | type = groupedTypes[OpTypeArray][t]; |
553 | if (type->getIdOperand(0) == element && |
554 | type->getIdOperand(1) == sizeId) |
555 | return type->getResultId(); |
556 | } |
557 | } |
558 | |
559 | // not found, make it |
560 | type = new Instruction(getUniqueId(), NoType, OpTypeArray); |
561 | type->addIdOperand(element); |
562 | type->addIdOperand(sizeId); |
563 | groupedTypes[OpTypeArray].push_back(type); |
564 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
565 | module.mapInstruction(type); |
566 | |
567 | if (emitNonSemanticShaderDebugInfo) |
568 | { |
569 | auto const debugResultId = makeArrayDebugType(element, sizeId); |
570 | debugId[type->getResultId()] = debugResultId; |
571 | } |
572 | |
573 | return type->getResultId(); |
574 | } |
575 | |
576 | Id Builder::makeRuntimeArray(Id element) |
577 | { |
578 | Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeRuntimeArray); |
579 | type->addIdOperand(element); |
580 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
581 | module.mapInstruction(type); |
582 | |
583 | if (emitNonSemanticShaderDebugInfo) |
584 | { |
585 | auto const debugResultId = makeArrayDebugType(element, makeUintConstant(0)); |
586 | debugId[type->getResultId()] = debugResultId; |
587 | } |
588 | |
589 | return type->getResultId(); |
590 | } |
591 | |
592 | Id Builder::makeFunctionType(Id returnType, const std::vector<Id>& paramTypes) |
593 | { |
594 | // try to find it |
595 | Instruction* type; |
596 | for (int t = 0; t < (int)groupedTypes[OpTypeFunction].size(); ++t) { |
597 | type = groupedTypes[OpTypeFunction][t]; |
598 | if (type->getIdOperand(0) != returnType || (int)paramTypes.size() != type->getNumOperands() - 1) |
599 | continue; |
600 | bool mismatch = false; |
601 | for (int p = 0; p < (int)paramTypes.size(); ++p) { |
602 | if (paramTypes[p] != type->getIdOperand(p + 1)) { |
603 | mismatch = true; |
604 | break; |
605 | } |
606 | } |
607 | if (! mismatch) |
608 | { |
609 | // If compiling HLSL, glslang will create a wrapper function around the entrypoint. Accordingly, a void(void) |
610 | // function type is created for the wrapper function. However, nonsemantic shader debug information is disabled |
611 | // while creating the HLSL wrapper. Consequently, if we encounter another void(void) function, we need to create |
612 | // the associated debug function type if it hasn't been created yet. |
613 | if(emitNonSemanticShaderDebugInfo && debugId[type->getResultId()] == 0) { |
614 | assert(sourceLang == spv::SourceLanguageHLSL); |
615 | assert(getTypeClass(returnType) == OpTypeVoid && paramTypes.size() == 0); |
616 | |
617 | Id debugTypeId = makeDebugFunctionType(returnType, {}); |
618 | debugId[type->getResultId()] = debugTypeId; |
619 | } |
620 | return type->getResultId(); |
621 | } |
622 | } |
623 | |
624 | // not found, make it |
625 | Id typeId = getUniqueId(); |
626 | type = new Instruction(typeId, NoType, OpTypeFunction); |
627 | type->addIdOperand(returnType); |
628 | for (int p = 0; p < (int)paramTypes.size(); ++p) |
629 | type->addIdOperand(paramTypes[p]); |
630 | groupedTypes[OpTypeFunction].push_back(type); |
631 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
632 | module.mapInstruction(type); |
633 | |
634 | // make debug type and map it |
635 | if (emitNonSemanticShaderDebugInfo) { |
636 | Id debugTypeId = makeDebugFunctionType(returnType, paramTypes); |
637 | debugId[typeId] = debugTypeId; |
638 | } |
639 | |
640 | return type->getResultId(); |
641 | } |
642 | |
643 | Id Builder::makeDebugFunctionType(Id returnType, const std::vector<Id>& paramTypes) |
644 | { |
645 | assert(debugId[returnType] != 0); |
646 | |
647 | Id typeId = getUniqueId(); |
648 | auto type = new Instruction(typeId, makeVoidType(), OpExtInst); |
649 | type->addIdOperand(nonSemanticShaderDebugInfo); |
650 | type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeFunction); |
651 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic)); |
652 | type->addIdOperand(debugId[returnType]); |
653 | for (auto const paramType : paramTypes) { |
654 | if (isPointerType(paramType) || isArrayType(paramType)) { |
655 | type->addIdOperand(debugId[getContainedTypeId(paramType)]); |
656 | } |
657 | else { |
658 | type->addIdOperand(debugId[paramType]); |
659 | } |
660 | } |
661 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
662 | module.mapInstruction(type); |
663 | return typeId; |
664 | } |
665 | |
666 | Id Builder::makeImageType(Id sampledType, Dim dim, bool depth, bool arrayed, bool ms, unsigned sampled, |
667 | ImageFormat format) |
668 | { |
669 | assert(sampled == 1 || sampled == 2); |
670 | |
671 | // try to find it |
672 | Instruction* type; |
673 | for (int t = 0; t < (int)groupedTypes[OpTypeImage].size(); ++t) { |
674 | type = groupedTypes[OpTypeImage][t]; |
675 | if (type->getIdOperand(0) == sampledType && |
676 | type->getImmediateOperand(1) == (unsigned int)dim && |
677 | type->getImmediateOperand(2) == ( depth ? 1u : 0u) && |
678 | type->getImmediateOperand(3) == (arrayed ? 1u : 0u) && |
679 | type->getImmediateOperand(4) == ( ms ? 1u : 0u) && |
680 | type->getImmediateOperand(5) == sampled && |
681 | type->getImmediateOperand(6) == (unsigned int)format) |
682 | return type->getResultId(); |
683 | } |
684 | |
685 | // not found, make it |
686 | type = new Instruction(getUniqueId(), NoType, OpTypeImage); |
687 | type->addIdOperand(sampledType); |
688 | type->addImmediateOperand( dim); |
689 | type->addImmediateOperand( depth ? 1 : 0); |
690 | type->addImmediateOperand(arrayed ? 1 : 0); |
691 | type->addImmediateOperand( ms ? 1 : 0); |
692 | type->addImmediateOperand(sampled); |
693 | type->addImmediateOperand((unsigned int)format); |
694 | |
695 | groupedTypes[OpTypeImage].push_back(type); |
696 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
697 | module.mapInstruction(type); |
698 | |
699 | #ifndef GLSLANG_WEB |
700 | // deal with capabilities |
701 | switch (dim) { |
702 | case DimBuffer: |
703 | if (sampled == 1) |
704 | addCapability(CapabilitySampledBuffer); |
705 | else |
706 | addCapability(CapabilityImageBuffer); |
707 | break; |
708 | case Dim1D: |
709 | if (sampled == 1) |
710 | addCapability(CapabilitySampled1D); |
711 | else |
712 | addCapability(CapabilityImage1D); |
713 | break; |
714 | case DimCube: |
715 | if (arrayed) { |
716 | if (sampled == 1) |
717 | addCapability(CapabilitySampledCubeArray); |
718 | else |
719 | addCapability(CapabilityImageCubeArray); |
720 | } |
721 | break; |
722 | case DimRect: |
723 | if (sampled == 1) |
724 | addCapability(CapabilitySampledRect); |
725 | else |
726 | addCapability(CapabilityImageRect); |
727 | break; |
728 | case DimSubpassData: |
729 | addCapability(CapabilityInputAttachment); |
730 | break; |
731 | default: |
732 | break; |
733 | } |
734 | |
735 | if (ms) { |
736 | if (sampled == 2) { |
737 | // Images used with subpass data are not storage |
738 | // images, so don't require the capability for them. |
739 | if (dim != Dim::DimSubpassData) |
740 | addCapability(CapabilityStorageImageMultisample); |
741 | if (arrayed) |
742 | addCapability(CapabilityImageMSArray); |
743 | } |
744 | } |
745 | #endif |
746 | |
747 | if (emitNonSemanticShaderDebugInfo) |
748 | { |
749 | auto TypeName = [&dim]() -> char const* { |
750 | switch (dim) { |
751 | case Dim1D: return "type.1d.image" ; |
752 | case Dim2D: return "type.2d.image" ; |
753 | case Dim3D: return "type.3d.image" ; |
754 | case DimCube: return "type.cube.image" ; |
755 | default: return "type.image" ; |
756 | } |
757 | }; |
758 | |
759 | auto const debugResultId = makeCompositeDebugType({}, TypeName(), NonSemanticShaderDebugInfo100Class, true); |
760 | debugId[type->getResultId()] = debugResultId; |
761 | } |
762 | |
763 | return type->getResultId(); |
764 | } |
765 | |
766 | Id Builder::makeSampledImageType(Id imageType) |
767 | { |
768 | // try to find it |
769 | Instruction* type; |
770 | for (int t = 0; t < (int)groupedTypes[OpTypeSampledImage].size(); ++t) { |
771 | type = groupedTypes[OpTypeSampledImage][t]; |
772 | if (type->getIdOperand(0) == imageType) |
773 | return type->getResultId(); |
774 | } |
775 | |
776 | // not found, make it |
777 | type = new Instruction(getUniqueId(), NoType, OpTypeSampledImage); |
778 | type->addIdOperand(imageType); |
779 | |
780 | groupedTypes[OpTypeSampledImage].push_back(type); |
781 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
782 | module.mapInstruction(type); |
783 | |
784 | if (emitNonSemanticShaderDebugInfo) |
785 | { |
786 | auto const debugResultId = makeCompositeDebugType({}, "type.sampled.image" , NonSemanticShaderDebugInfo100Class, true); |
787 | debugId[type->getResultId()] = debugResultId; |
788 | } |
789 | |
790 | return type->getResultId(); |
791 | } |
792 | |
793 | Id Builder::makeDebugInfoNone() |
794 | { |
795 | if (debugInfoNone != 0) |
796 | return debugInfoNone; |
797 | |
798 | Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
799 | inst->addIdOperand(nonSemanticShaderDebugInfo); |
800 | inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugInfoNone); |
801 | |
802 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst)); |
803 | module.mapInstruction(inst); |
804 | |
805 | debugInfoNone = inst->getResultId(); |
806 | |
807 | return debugInfoNone; |
808 | } |
809 | |
810 | Id Builder::makeBoolDebugType(int const size) |
811 | { |
812 | // try to find it |
813 | Instruction* type; |
814 | for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].size(); ++t) { |
815 | type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic][t]; |
816 | if (type->getIdOperand(0) == getStringId("bool" ) && |
817 | type->getIdOperand(1) == static_cast<unsigned int>(size) && |
818 | type->getIdOperand(2) == NonSemanticShaderDebugInfo100Boolean) |
819 | return type->getResultId(); |
820 | } |
821 | |
822 | type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
823 | type->addIdOperand(nonSemanticShaderDebugInfo); |
824 | type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeBasic); |
825 | |
826 | type->addIdOperand(getStringId("bool" )); // name id |
827 | type->addIdOperand(makeUintConstant(size)); // size id |
828 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Boolean)); // encoding id |
829 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100None)); // flags id |
830 | |
831 | groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].push_back(type); |
832 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
833 | module.mapInstruction(type); |
834 | |
835 | return type->getResultId(); |
836 | } |
837 | |
838 | Id Builder::makeIntegerDebugType(int const width, bool const hasSign) |
839 | { |
840 | // try to find it |
841 | Instruction* type; |
842 | for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].size(); ++t) { |
843 | type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic][t]; |
844 | if (type->getIdOperand(0) == (hasSign ? getStringId("int" ) : getStringId("uint" )) && |
845 | type->getIdOperand(1) == static_cast<unsigned int>(width) && |
846 | type->getIdOperand(2) == (hasSign ? NonSemanticShaderDebugInfo100Signed : NonSemanticShaderDebugInfo100Unsigned)) |
847 | return type->getResultId(); |
848 | } |
849 | |
850 | // not found, make it |
851 | type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
852 | type->addIdOperand(nonSemanticShaderDebugInfo); |
853 | type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeBasic); |
854 | if(hasSign == true) { |
855 | type->addIdOperand(getStringId("int" )); // name id |
856 | } else { |
857 | type->addIdOperand(getStringId("uint" )); // name id |
858 | } |
859 | type->addIdOperand(makeUintConstant(width)); // size id |
860 | if(hasSign == true) { |
861 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Signed)); // encoding id |
862 | } else { |
863 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Unsigned)); // encoding id |
864 | } |
865 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100None)); // flags id |
866 | |
867 | groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].push_back(type); |
868 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
869 | module.mapInstruction(type); |
870 | |
871 | return type->getResultId(); |
872 | } |
873 | |
874 | Id Builder::makeFloatDebugType(int const width) |
875 | { |
876 | // try to find it |
877 | Instruction* type; |
878 | for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].size(); ++t) { |
879 | type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic][t]; |
880 | if (type->getIdOperand(0) == getStringId("float" ) && |
881 | type->getIdOperand(1) == static_cast<unsigned int>(width) && |
882 | type->getIdOperand(2) == NonSemanticShaderDebugInfo100Float) |
883 | return type->getResultId(); |
884 | } |
885 | |
886 | // not found, make it |
887 | type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
888 | type->addIdOperand(nonSemanticShaderDebugInfo); |
889 | type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeBasic); |
890 | type->addIdOperand(getStringId("float" )); // name id |
891 | type->addIdOperand(makeUintConstant(width)); // size id |
892 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Float)); // encoding id |
893 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100None)); // flags id |
894 | |
895 | groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].push_back(type); |
896 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
897 | module.mapInstruction(type); |
898 | |
899 | return type->getResultId(); |
900 | } |
901 | |
902 | Id Builder::makeSequentialDebugType(Id const baseType, Id const componentCount, NonSemanticShaderDebugInfo100Instructions const sequenceType) |
903 | { |
904 | assert(sequenceType == NonSemanticShaderDebugInfo100DebugTypeArray || |
905 | sequenceType == NonSemanticShaderDebugInfo100DebugTypeVector); |
906 | |
907 | // try to find it |
908 | Instruction* type; |
909 | for (int t = 0; t < (int)groupedDebugTypes[sequenceType].size(); ++t) { |
910 | type = groupedDebugTypes[sequenceType][t]; |
911 | if (type->getIdOperand(0) == baseType && |
912 | type->getIdOperand(1) == makeUintConstant(componentCount)) |
913 | return type->getResultId(); |
914 | } |
915 | |
916 | // not found, make it |
917 | type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
918 | type->addIdOperand(nonSemanticShaderDebugInfo); |
919 | type->addImmediateOperand(sequenceType); |
920 | type->addIdOperand(debugId[baseType]); // base type |
921 | type->addIdOperand(componentCount); // component count |
922 | |
923 | groupedDebugTypes[sequenceType].push_back(type); |
924 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
925 | module.mapInstruction(type); |
926 | |
927 | return type->getResultId(); |
928 | } |
929 | |
930 | Id Builder::makeArrayDebugType(Id const baseType, Id const componentCount) |
931 | { |
932 | return makeSequentialDebugType(baseType, componentCount, NonSemanticShaderDebugInfo100DebugTypeArray); |
933 | } |
934 | |
935 | Id Builder::makeVectorDebugType(Id const baseType, int const componentCount) |
936 | { |
937 | return makeSequentialDebugType(baseType, makeUintConstant(componentCount), NonSemanticShaderDebugInfo100DebugTypeVector);; |
938 | } |
939 | |
940 | Id Builder::makeMatrixDebugType(Id const vectorType, int const vectorCount, bool columnMajor) |
941 | { |
942 | // try to find it |
943 | Instruction* type; |
944 | for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMatrix].size(); ++t) { |
945 | type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMatrix][t]; |
946 | if (type->getIdOperand(0) == vectorType && |
947 | type->getIdOperand(1) == makeUintConstant(vectorCount)) |
948 | return type->getResultId(); |
949 | } |
950 | |
951 | // not found, make it |
952 | type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
953 | type->addIdOperand(nonSemanticShaderDebugInfo); |
954 | type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeMatrix); |
955 | type->addIdOperand(debugId[vectorType]); // vector type id |
956 | type->addIdOperand(makeUintConstant(vectorCount)); // component count id |
957 | type->addIdOperand(makeBoolConstant(columnMajor)); // column-major id |
958 | |
959 | groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMatrix].push_back(type); |
960 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
961 | module.mapInstruction(type); |
962 | |
963 | return type->getResultId(); |
964 | } |
965 | |
966 | Id Builder::makeMemberDebugType(Id const memberType, DebugTypeLoc const& debugTypeLoc) |
967 | { |
968 | assert(debugId[memberType] != 0); |
969 | |
970 | Instruction* type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
971 | type->addIdOperand(nonSemanticShaderDebugInfo); |
972 | type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeMember); |
973 | type->addIdOperand(getStringId(debugTypeLoc.name)); // name id |
974 | type->addIdOperand(debugId[memberType]); // type id |
975 | type->addIdOperand(makeDebugSource(sourceFileStringId)); // source id TODO: verify this works across include directives |
976 | type->addIdOperand(makeUintConstant(debugTypeLoc.line)); // line id TODO: currentLine is always zero |
977 | type->addIdOperand(makeUintConstant(debugTypeLoc.column)); // TODO: column id |
978 | type->addIdOperand(makeUintConstant(0)); // TODO: offset id |
979 | type->addIdOperand(makeUintConstant(0)); // TODO: size id |
980 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic)); // flags id |
981 | |
982 | groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMember].push_back(type); |
983 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
984 | module.mapInstruction(type); |
985 | |
986 | return type->getResultId(); |
987 | } |
988 | |
989 | // Note: To represent a source language opaque type, this instruction must have no Members operands, Size operand must be |
990 | // DebugInfoNone, and Name must start with @ to avoid clashes with user defined names. |
991 | Id Builder::makeCompositeDebugType(std::vector<Id> const& memberTypes, char const*const name, |
992 | NonSemanticShaderDebugInfo100DebugCompositeType const tag, bool const isOpaqueType) |
993 | { |
994 | // Create the debug member types. |
995 | std::vector<Id> memberDebugTypes; |
996 | for(auto const memberType : memberTypes) { |
997 | assert(debugTypeLocs.find(memberType) != debugTypeLocs.end()); |
998 | |
999 | memberDebugTypes.emplace_back(makeMemberDebugType(memberType, debugTypeLocs[memberType])); |
1000 | |
1001 | // TODO: Need to rethink this method of passing location information. |
1002 | // debugTypeLocs.erase(memberType); |
1003 | } |
1004 | |
1005 | // Create The structure debug type. |
1006 | Instruction* type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
1007 | type->addIdOperand(nonSemanticShaderDebugInfo); |
1008 | type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeComposite); |
1009 | type->addIdOperand(getStringId(name)); // name id |
1010 | type->addIdOperand(makeUintConstant(tag)); // tag id |
1011 | type->addIdOperand(makeDebugSource(sourceFileStringId)); // source id TODO: verify this works across include directives |
1012 | type->addIdOperand(makeUintConstant(currentLine)); // line id TODO: currentLine always zero? |
1013 | type->addIdOperand(makeUintConstant(0)); // TODO: column id |
1014 | type->addIdOperand(makeDebugCompilationUnit()); // scope id |
1015 | if(isOpaqueType == true) { |
1016 | // Prepend '@' to opaque types. |
1017 | type->addIdOperand(getStringId('@' + std::string(name))); // linkage name id |
1018 | type->addIdOperand(makeDebugInfoNone()); // size id |
1019 | } else { |
1020 | type->addIdOperand(getStringId(name)); // linkage name id |
1021 | type->addIdOperand(makeUintConstant(0)); // TODO: size id |
1022 | } |
1023 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic)); // flags id |
1024 | assert(isOpaqueType == false || (isOpaqueType == true && memberDebugTypes.empty())); |
1025 | for(auto const memberDebugType : memberDebugTypes) { |
1026 | type->addIdOperand(memberDebugType); |
1027 | } |
1028 | |
1029 | groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeComposite].push_back(type); |
1030 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
1031 | module.mapInstruction(type); |
1032 | |
1033 | return type->getResultId(); |
1034 | } |
1035 | |
1036 | Id Builder::makeDebugSource(const Id fileName) { |
1037 | if (debugSourceId.find(fileName) != debugSourceId.end()) |
1038 | return debugSourceId[fileName]; |
1039 | spv::Id resultId = getUniqueId(); |
1040 | Instruction* sourceInst = new Instruction(resultId, makeVoidType(), OpExtInst); |
1041 | sourceInst->addIdOperand(nonSemanticShaderDebugInfo); |
1042 | sourceInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugSource); |
1043 | sourceInst->addIdOperand(fileName); |
1044 | if (emitNonSemanticShaderDebugSource) { |
1045 | spv::Id sourceId = 0; |
1046 | if (fileName == sourceFileStringId) { |
1047 | sourceId = getStringId(sourceText); |
1048 | } else { |
1049 | auto incItr = includeFiles.find(fileName); |
1050 | assert(incItr != includeFiles.end()); |
1051 | sourceId = getStringId(*incItr->second); |
1052 | } |
1053 | sourceInst->addIdOperand(sourceId); |
1054 | } |
1055 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(sourceInst)); |
1056 | module.mapInstruction(sourceInst); |
1057 | debugSourceId[fileName] = resultId; |
1058 | return resultId; |
1059 | } |
1060 | |
1061 | Id Builder::makeDebugCompilationUnit() { |
1062 | if (nonSemanticShaderCompilationUnitId != 0) |
1063 | return nonSemanticShaderCompilationUnitId; |
1064 | spv::Id resultId = getUniqueId(); |
1065 | Instruction* sourceInst = new Instruction(resultId, makeVoidType(), OpExtInst); |
1066 | sourceInst->addIdOperand(nonSemanticShaderDebugInfo); |
1067 | sourceInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugCompilationUnit); |
1068 | sourceInst->addIdOperand(makeUintConstant(1)); // TODO(greg-lunarg): Get rid of magic number |
1069 | sourceInst->addIdOperand(makeUintConstant(4)); // TODO(greg-lunarg): Get rid of magic number |
1070 | sourceInst->addIdOperand(makeDebugSource(sourceFileStringId)); |
1071 | sourceInst->addIdOperand(makeUintConstant(sourceLang)); |
1072 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(sourceInst)); |
1073 | module.mapInstruction(sourceInst); |
1074 | nonSemanticShaderCompilationUnitId = resultId; |
1075 | |
1076 | // We can reasonably assume that makeDebugCompilationUnit will be called before any of |
1077 | // debug-scope stack. Function scopes and lexical scopes will occur afterward. |
1078 | assert(currentDebugScopeId.empty()); |
1079 | currentDebugScopeId.push(nonSemanticShaderCompilationUnitId); |
1080 | |
1081 | return resultId; |
1082 | } |
1083 | |
1084 | Id Builder::createDebugGlobalVariable(Id const type, char const*const name, Id const variable) |
1085 | { |
1086 | assert(type != 0); |
1087 | |
1088 | Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
1089 | inst->addIdOperand(nonSemanticShaderDebugInfo); |
1090 | inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugGlobalVariable); |
1091 | inst->addIdOperand(getStringId(name)); // name id |
1092 | inst->addIdOperand(type); // type id |
1093 | inst->addIdOperand(makeDebugSource(sourceFileStringId)); // source id |
1094 | inst->addIdOperand(makeUintConstant(currentLine)); // line id TODO: currentLine always zero? |
1095 | inst->addIdOperand(makeUintConstant(0)); // TODO: column id |
1096 | inst->addIdOperand(makeDebugCompilationUnit()); // scope id |
1097 | inst->addIdOperand(getStringId(name)); // linkage name id |
1098 | inst->addIdOperand(variable); // variable id |
1099 | inst->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsDefinition)); // flags id |
1100 | |
1101 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst)); |
1102 | module.mapInstruction(inst); |
1103 | |
1104 | return inst->getResultId(); |
1105 | } |
1106 | |
1107 | Id Builder::createDebugLocalVariable(Id type, char const*const name, size_t const argNumber) |
1108 | { |
1109 | assert(name != nullptr); |
1110 | assert(!currentDebugScopeId.empty()); |
1111 | |
1112 | Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
1113 | inst->addIdOperand(nonSemanticShaderDebugInfo); |
1114 | inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugLocalVariable); |
1115 | inst->addIdOperand(getStringId(name)); // name id |
1116 | inst->addIdOperand(type); // type id |
1117 | inst->addIdOperand(makeDebugSource(sourceFileStringId)); // source id |
1118 | inst->addIdOperand(makeUintConstant(currentLine)); // line id |
1119 | inst->addIdOperand(makeUintConstant(0)); // TODO: column id |
1120 | inst->addIdOperand(currentDebugScopeId.top()); // scope id |
1121 | inst->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsLocal)); // flags id |
1122 | if(argNumber != 0) { |
1123 | inst->addIdOperand(makeUintConstant(argNumber)); |
1124 | } |
1125 | |
1126 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst)); |
1127 | module.mapInstruction(inst); |
1128 | |
1129 | return inst->getResultId(); |
1130 | } |
1131 | |
1132 | Id Builder::makeDebugExpression() |
1133 | { |
1134 | if (debugExpression != 0) |
1135 | return debugExpression; |
1136 | |
1137 | Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
1138 | inst->addIdOperand(nonSemanticShaderDebugInfo); |
1139 | inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugExpression); |
1140 | |
1141 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst)); |
1142 | module.mapInstruction(inst); |
1143 | |
1144 | debugExpression = inst->getResultId(); |
1145 | |
1146 | return debugExpression; |
1147 | } |
1148 | |
1149 | Id Builder::makeDebugDeclare(Id const debugLocalVariable, Id const localVariable) |
1150 | { |
1151 | Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
1152 | inst->addIdOperand(nonSemanticShaderDebugInfo); |
1153 | inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugDeclare); |
1154 | inst->addIdOperand(debugLocalVariable); // debug local variable id |
1155 | inst->addIdOperand(localVariable); // local variable id |
1156 | inst->addIdOperand(makeDebugExpression()); // expression id |
1157 | buildPoint->addInstruction(std::unique_ptr<Instruction>(inst)); |
1158 | |
1159 | return inst->getResultId(); |
1160 | } |
1161 | |
1162 | #ifndef GLSLANG_WEB |
1163 | Id Builder::makeAccelerationStructureType() |
1164 | { |
1165 | Instruction *type; |
1166 | if (groupedTypes[OpTypeAccelerationStructureKHR].size() == 0) { |
1167 | type = new Instruction(getUniqueId(), NoType, OpTypeAccelerationStructureKHR); |
1168 | groupedTypes[OpTypeAccelerationStructureKHR].push_back(type); |
1169 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
1170 | module.mapInstruction(type); |
1171 | } else { |
1172 | type = groupedTypes[OpTypeAccelerationStructureKHR].back(); |
1173 | } |
1174 | |
1175 | return type->getResultId(); |
1176 | } |
1177 | |
1178 | Id Builder::makeRayQueryType() |
1179 | { |
1180 | Instruction *type; |
1181 | if (groupedTypes[OpTypeRayQueryKHR].size() == 0) { |
1182 | type = new Instruction(getUniqueId(), NoType, OpTypeRayQueryKHR); |
1183 | groupedTypes[OpTypeRayQueryKHR].push_back(type); |
1184 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
1185 | module.mapInstruction(type); |
1186 | } else { |
1187 | type = groupedTypes[OpTypeRayQueryKHR].back(); |
1188 | } |
1189 | |
1190 | return type->getResultId(); |
1191 | } |
1192 | |
1193 | Id Builder::makeHitObjectNVType() |
1194 | { |
1195 | Instruction *type; |
1196 | if (groupedTypes[OpTypeHitObjectNV].size() == 0) { |
1197 | type = new Instruction(getUniqueId(), NoType, OpTypeHitObjectNV); |
1198 | groupedTypes[OpTypeHitObjectNV].push_back(type); |
1199 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
1200 | module.mapInstruction(type); |
1201 | } else { |
1202 | type = groupedTypes[OpTypeHitObjectNV].back(); |
1203 | } |
1204 | |
1205 | return type->getResultId(); |
1206 | } |
1207 | #endif |
1208 | |
1209 | Id Builder::getDerefTypeId(Id resultId) const |
1210 | { |
1211 | Id typeId = getTypeId(resultId); |
1212 | assert(isPointerType(typeId)); |
1213 | |
1214 | return module.getInstruction(typeId)->getIdOperand(1); |
1215 | } |
1216 | |
1217 | Op Builder::getMostBasicTypeClass(Id typeId) const |
1218 | { |
1219 | Instruction* instr = module.getInstruction(typeId); |
1220 | |
1221 | Op typeClass = instr->getOpCode(); |
1222 | switch (typeClass) |
1223 | { |
1224 | case OpTypeVector: |
1225 | case OpTypeMatrix: |
1226 | case OpTypeArray: |
1227 | case OpTypeRuntimeArray: |
1228 | return getMostBasicTypeClass(instr->getIdOperand(0)); |
1229 | case OpTypePointer: |
1230 | return getMostBasicTypeClass(instr->getIdOperand(1)); |
1231 | default: |
1232 | return typeClass; |
1233 | } |
1234 | } |
1235 | |
1236 | int Builder::getNumTypeConstituents(Id typeId) const |
1237 | { |
1238 | Instruction* instr = module.getInstruction(typeId); |
1239 | |
1240 | switch (instr->getOpCode()) |
1241 | { |
1242 | case OpTypeBool: |
1243 | case OpTypeInt: |
1244 | case OpTypeFloat: |
1245 | case OpTypePointer: |
1246 | return 1; |
1247 | case OpTypeVector: |
1248 | case OpTypeMatrix: |
1249 | return instr->getImmediateOperand(1); |
1250 | case OpTypeArray: |
1251 | { |
1252 | Id lengthId = instr->getIdOperand(1); |
1253 | return module.getInstruction(lengthId)->getImmediateOperand(0); |
1254 | } |
1255 | case OpTypeStruct: |
1256 | return instr->getNumOperands(); |
1257 | case OpTypeCooperativeMatrixNV: |
1258 | // has only one constituent when used with OpCompositeConstruct. |
1259 | return 1; |
1260 | default: |
1261 | assert(0); |
1262 | return 1; |
1263 | } |
1264 | } |
1265 | |
1266 | // Return the lowest-level type of scalar that an homogeneous composite is made out of. |
1267 | // Typically, this is just to find out if something is made out of ints or floats. |
1268 | // However, it includes returning a structure, if say, it is an array of structure. |
1269 | Id Builder::getScalarTypeId(Id typeId) const |
1270 | { |
1271 | Instruction* instr = module.getInstruction(typeId); |
1272 | |
1273 | Op typeClass = instr->getOpCode(); |
1274 | switch (typeClass) |
1275 | { |
1276 | case OpTypeVoid: |
1277 | case OpTypeBool: |
1278 | case OpTypeInt: |
1279 | case OpTypeFloat: |
1280 | case OpTypeStruct: |
1281 | return instr->getResultId(); |
1282 | case OpTypeVector: |
1283 | case OpTypeMatrix: |
1284 | case OpTypeArray: |
1285 | case OpTypeRuntimeArray: |
1286 | case OpTypePointer: |
1287 | return getScalarTypeId(getContainedTypeId(typeId)); |
1288 | default: |
1289 | assert(0); |
1290 | return NoResult; |
1291 | } |
1292 | } |
1293 | |
1294 | // Return the type of 'member' of a composite. |
1295 | Id Builder::getContainedTypeId(Id typeId, int member) const |
1296 | { |
1297 | Instruction* instr = module.getInstruction(typeId); |
1298 | |
1299 | Op typeClass = instr->getOpCode(); |
1300 | switch (typeClass) |
1301 | { |
1302 | case OpTypeVector: |
1303 | case OpTypeMatrix: |
1304 | case OpTypeArray: |
1305 | case OpTypeRuntimeArray: |
1306 | case OpTypeCooperativeMatrixNV: |
1307 | return instr->getIdOperand(0); |
1308 | case OpTypePointer: |
1309 | return instr->getIdOperand(1); |
1310 | case OpTypeStruct: |
1311 | return instr->getIdOperand(member); |
1312 | default: |
1313 | assert(0); |
1314 | return NoResult; |
1315 | } |
1316 | } |
1317 | |
1318 | // Figure out the final resulting type of the access chain. |
1319 | Id Builder::getResultingAccessChainType() const |
1320 | { |
1321 | assert(accessChain.base != NoResult); |
1322 | Id typeId = getTypeId(accessChain.base); |
1323 | |
1324 | assert(isPointerType(typeId)); |
1325 | typeId = getContainedTypeId(typeId); |
1326 | |
1327 | for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) { |
1328 | if (isStructType(typeId)) { |
1329 | assert(isConstantScalar(accessChain.indexChain[i])); |
1330 | typeId = getContainedTypeId(typeId, getConstantScalar(accessChain.indexChain[i])); |
1331 | } else |
1332 | typeId = getContainedTypeId(typeId, accessChain.indexChain[i]); |
1333 | } |
1334 | |
1335 | return typeId; |
1336 | } |
1337 | |
1338 | // Return the immediately contained type of a given composite type. |
1339 | Id Builder::getContainedTypeId(Id typeId) const |
1340 | { |
1341 | return getContainedTypeId(typeId, 0); |
1342 | } |
1343 | |
1344 | // Returns true if 'typeId' is or contains a scalar type declared with 'typeOp' |
1345 | // of width 'width'. The 'width' is only consumed for int and float types. |
1346 | // Returns false otherwise. |
1347 | bool Builder::containsType(Id typeId, spv::Op typeOp, unsigned int width) const |
1348 | { |
1349 | const Instruction& instr = *module.getInstruction(typeId); |
1350 | |
1351 | Op typeClass = instr.getOpCode(); |
1352 | switch (typeClass) |
1353 | { |
1354 | case OpTypeInt: |
1355 | case OpTypeFloat: |
1356 | return typeClass == typeOp && instr.getImmediateOperand(0) == width; |
1357 | case OpTypeStruct: |
1358 | for (int m = 0; m < instr.getNumOperands(); ++m) { |
1359 | if (containsType(instr.getIdOperand(m), typeOp, width)) |
1360 | return true; |
1361 | } |
1362 | return false; |
1363 | case OpTypePointer: |
1364 | return false; |
1365 | case OpTypeVector: |
1366 | case OpTypeMatrix: |
1367 | case OpTypeArray: |
1368 | case OpTypeRuntimeArray: |
1369 | return containsType(getContainedTypeId(typeId), typeOp, width); |
1370 | default: |
1371 | return typeClass == typeOp; |
1372 | } |
1373 | } |
1374 | |
1375 | // return true if the type is a pointer to PhysicalStorageBufferEXT or an |
1376 | // array of such pointers. These require restrict/aliased decorations. |
1377 | bool Builder::containsPhysicalStorageBufferOrArray(Id typeId) const |
1378 | { |
1379 | const Instruction& instr = *module.getInstruction(typeId); |
1380 | |
1381 | Op typeClass = instr.getOpCode(); |
1382 | switch (typeClass) |
1383 | { |
1384 | case OpTypePointer: |
1385 | return getTypeStorageClass(typeId) == StorageClassPhysicalStorageBufferEXT; |
1386 | case OpTypeArray: |
1387 | return containsPhysicalStorageBufferOrArray(getContainedTypeId(typeId)); |
1388 | default: |
1389 | return false; |
1390 | } |
1391 | } |
1392 | |
1393 | // See if a scalar constant of this type has already been created, so it |
1394 | // can be reused rather than duplicated. (Required by the specification). |
1395 | Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value) |
1396 | { |
1397 | Instruction* constant; |
1398 | for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { |
1399 | constant = groupedConstants[typeClass][i]; |
1400 | if (constant->getOpCode() == opcode && |
1401 | constant->getTypeId() == typeId && |
1402 | constant->getImmediateOperand(0) == value) |
1403 | return constant->getResultId(); |
1404 | } |
1405 | |
1406 | return 0; |
1407 | } |
1408 | |
1409 | // Version of findScalarConstant (see above) for scalars that take two operands (e.g. a 'double' or 'int64'). |
1410 | Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2) |
1411 | { |
1412 | Instruction* constant; |
1413 | for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { |
1414 | constant = groupedConstants[typeClass][i]; |
1415 | if (constant->getOpCode() == opcode && |
1416 | constant->getTypeId() == typeId && |
1417 | constant->getImmediateOperand(0) == v1 && |
1418 | constant->getImmediateOperand(1) == v2) |
1419 | return constant->getResultId(); |
1420 | } |
1421 | |
1422 | return 0; |
1423 | } |
1424 | |
1425 | // Return true if consuming 'opcode' means consuming a constant. |
1426 | // "constant" here means after final transform to executable code, |
1427 | // the value consumed will be a constant, so includes specialization. |
1428 | bool Builder::isConstantOpCode(Op opcode) const |
1429 | { |
1430 | switch (opcode) { |
1431 | case OpUndef: |
1432 | case OpConstantTrue: |
1433 | case OpConstantFalse: |
1434 | case OpConstant: |
1435 | case OpConstantComposite: |
1436 | case OpConstantSampler: |
1437 | case OpConstantNull: |
1438 | case OpSpecConstantTrue: |
1439 | case OpSpecConstantFalse: |
1440 | case OpSpecConstant: |
1441 | case OpSpecConstantComposite: |
1442 | case OpSpecConstantOp: |
1443 | return true; |
1444 | default: |
1445 | return false; |
1446 | } |
1447 | } |
1448 | |
1449 | // Return true if consuming 'opcode' means consuming a specialization constant. |
1450 | bool Builder::isSpecConstantOpCode(Op opcode) const |
1451 | { |
1452 | switch (opcode) { |
1453 | case OpSpecConstantTrue: |
1454 | case OpSpecConstantFalse: |
1455 | case OpSpecConstant: |
1456 | case OpSpecConstantComposite: |
1457 | case OpSpecConstantOp: |
1458 | return true; |
1459 | default: |
1460 | return false; |
1461 | } |
1462 | } |
1463 | |
1464 | bool Builder::isRayTracingOpCode(Op opcode) const |
1465 | { |
1466 | switch (opcode) { |
1467 | case OpTypeAccelerationStructureKHR: |
1468 | case OpTypeRayQueryKHR: |
1469 | return true; |
1470 | default: |
1471 | return false; |
1472 | } |
1473 | } |
1474 | |
1475 | Id Builder::makeNullConstant(Id typeId) |
1476 | { |
1477 | Instruction* constant; |
1478 | |
1479 | // See if we already made it. |
1480 | Id existing = NoResult; |
1481 | for (int i = 0; i < (int)nullConstants.size(); ++i) { |
1482 | constant = nullConstants[i]; |
1483 | if (constant->getTypeId() == typeId) |
1484 | existing = constant->getResultId(); |
1485 | } |
1486 | |
1487 | if (existing != NoResult) |
1488 | return existing; |
1489 | |
1490 | // Make it |
1491 | Instruction* c = new Instruction(getUniqueId(), typeId, OpConstantNull); |
1492 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); |
1493 | nullConstants.push_back(c); |
1494 | module.mapInstruction(c); |
1495 | |
1496 | return c->getResultId(); |
1497 | } |
1498 | |
1499 | Id Builder::makeBoolConstant(bool b, bool specConstant) |
1500 | { |
1501 | Id typeId = makeBoolType(); |
1502 | Instruction* constant; |
1503 | Op opcode = specConstant ? (b ? OpSpecConstantTrue : OpSpecConstantFalse) : (b ? OpConstantTrue : OpConstantFalse); |
1504 | |
1505 | // See if we already made it. Applies only to regular constants, because specialization constants |
1506 | // must remain distinct for the purpose of applying a SpecId decoration. |
1507 | if (! specConstant) { |
1508 | Id existing = 0; |
1509 | for (int i = 0; i < (int)groupedConstants[OpTypeBool].size(); ++i) { |
1510 | constant = groupedConstants[OpTypeBool][i]; |
1511 | if (constant->getTypeId() == typeId && constant->getOpCode() == opcode) |
1512 | existing = constant->getResultId(); |
1513 | } |
1514 | |
1515 | if (existing) |
1516 | return existing; |
1517 | } |
1518 | |
1519 | // Make it |
1520 | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
1521 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); |
1522 | groupedConstants[OpTypeBool].push_back(c); |
1523 | module.mapInstruction(c); |
1524 | |
1525 | return c->getResultId(); |
1526 | } |
1527 | |
1528 | Id Builder::makeIntConstant(Id typeId, unsigned value, bool specConstant) |
1529 | { |
1530 | Op opcode = specConstant ? OpSpecConstant : OpConstant; |
1531 | |
1532 | // See if we already made it. Applies only to regular constants, because specialization constants |
1533 | // must remain distinct for the purpose of applying a SpecId decoration. |
1534 | if (! specConstant) { |
1535 | Id existing = findScalarConstant(OpTypeInt, opcode, typeId, value); |
1536 | if (existing) |
1537 | return existing; |
1538 | } |
1539 | |
1540 | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
1541 | c->addImmediateOperand(value); |
1542 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); |
1543 | groupedConstants[OpTypeInt].push_back(c); |
1544 | module.mapInstruction(c); |
1545 | |
1546 | return c->getResultId(); |
1547 | } |
1548 | |
1549 | Id Builder::makeInt64Constant(Id typeId, unsigned long long value, bool specConstant) |
1550 | { |
1551 | Op opcode = specConstant ? OpSpecConstant : OpConstant; |
1552 | |
1553 | unsigned op1 = value & 0xFFFFFFFF; |
1554 | unsigned op2 = value >> 32; |
1555 | |
1556 | // See if we already made it. Applies only to regular constants, because specialization constants |
1557 | // must remain distinct for the purpose of applying a SpecId decoration. |
1558 | if (! specConstant) { |
1559 | Id existing = findScalarConstant(OpTypeInt, opcode, typeId, op1, op2); |
1560 | if (existing) |
1561 | return existing; |
1562 | } |
1563 | |
1564 | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
1565 | c->addImmediateOperand(op1); |
1566 | c->addImmediateOperand(op2); |
1567 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); |
1568 | groupedConstants[OpTypeInt].push_back(c); |
1569 | module.mapInstruction(c); |
1570 | |
1571 | return c->getResultId(); |
1572 | } |
1573 | |
1574 | Id Builder::makeFloatConstant(float f, bool specConstant) |
1575 | { |
1576 | Op opcode = specConstant ? OpSpecConstant : OpConstant; |
1577 | Id typeId = makeFloatType(32); |
1578 | union { float fl; unsigned int ui; } u; |
1579 | u.fl = f; |
1580 | unsigned value = u.ui; |
1581 | |
1582 | // See if we already made it. Applies only to regular constants, because specialization constants |
1583 | // must remain distinct for the purpose of applying a SpecId decoration. |
1584 | if (! specConstant) { |
1585 | Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value); |
1586 | if (existing) |
1587 | return existing; |
1588 | } |
1589 | |
1590 | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
1591 | c->addImmediateOperand(value); |
1592 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); |
1593 | groupedConstants[OpTypeFloat].push_back(c); |
1594 | module.mapInstruction(c); |
1595 | |
1596 | return c->getResultId(); |
1597 | } |
1598 | |
1599 | Id Builder::makeDoubleConstant(double d, bool specConstant) |
1600 | { |
1601 | #ifdef GLSLANG_WEB |
1602 | assert(0); |
1603 | return NoResult; |
1604 | #else |
1605 | Op opcode = specConstant ? OpSpecConstant : OpConstant; |
1606 | Id typeId = makeFloatType(64); |
1607 | union { double db; unsigned long long ull; } u; |
1608 | u.db = d; |
1609 | unsigned long long value = u.ull; |
1610 | unsigned op1 = value & 0xFFFFFFFF; |
1611 | unsigned op2 = value >> 32; |
1612 | |
1613 | // See if we already made it. Applies only to regular constants, because specialization constants |
1614 | // must remain distinct for the purpose of applying a SpecId decoration. |
1615 | if (! specConstant) { |
1616 | Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, op1, op2); |
1617 | if (existing) |
1618 | return existing; |
1619 | } |
1620 | |
1621 | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
1622 | c->addImmediateOperand(op1); |
1623 | c->addImmediateOperand(op2); |
1624 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); |
1625 | groupedConstants[OpTypeFloat].push_back(c); |
1626 | module.mapInstruction(c); |
1627 | |
1628 | return c->getResultId(); |
1629 | #endif |
1630 | } |
1631 | |
1632 | Id Builder::makeFloat16Constant(float f16, bool specConstant) |
1633 | { |
1634 | #ifdef GLSLANG_WEB |
1635 | assert(0); |
1636 | return NoResult; |
1637 | #else |
1638 | Op opcode = specConstant ? OpSpecConstant : OpConstant; |
1639 | Id typeId = makeFloatType(16); |
1640 | |
1641 | spvutils::HexFloat<spvutils::FloatProxy<float>> fVal(f16); |
1642 | spvutils::HexFloat<spvutils::FloatProxy<spvutils::Float16>> f16Val(0); |
1643 | fVal.castTo(f16Val, spvutils::kRoundToZero); |
1644 | |
1645 | unsigned value = f16Val.value().getAsFloat().get_value(); |
1646 | |
1647 | // See if we already made it. Applies only to regular constants, because specialization constants |
1648 | // must remain distinct for the purpose of applying a SpecId decoration. |
1649 | if (!specConstant) { |
1650 | Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value); |
1651 | if (existing) |
1652 | return existing; |
1653 | } |
1654 | |
1655 | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
1656 | c->addImmediateOperand(value); |
1657 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); |
1658 | groupedConstants[OpTypeFloat].push_back(c); |
1659 | module.mapInstruction(c); |
1660 | |
1661 | return c->getResultId(); |
1662 | #endif |
1663 | } |
1664 | |
1665 | Id Builder::makeFpConstant(Id type, double d, bool specConstant) |
1666 | { |
1667 | #ifdef GLSLANG_WEB |
1668 | const int width = 32; |
1669 | assert(width == getScalarTypeWidth(type)); |
1670 | #else |
1671 | const int width = getScalarTypeWidth(type); |
1672 | #endif |
1673 | |
1674 | assert(isFloatType(type)); |
1675 | |
1676 | switch (width) { |
1677 | case 16: |
1678 | return makeFloat16Constant((float)d, specConstant); |
1679 | case 32: |
1680 | return makeFloatConstant((float)d, specConstant); |
1681 | case 64: |
1682 | return makeDoubleConstant(d, specConstant); |
1683 | default: |
1684 | break; |
1685 | } |
1686 | |
1687 | assert(false); |
1688 | return NoResult; |
1689 | } |
1690 | |
1691 | Id Builder::importNonSemanticShaderDebugInfoInstructions() |
1692 | { |
1693 | assert(emitNonSemanticShaderDebugInfo == true); |
1694 | |
1695 | if(nonSemanticShaderDebugInfo == 0) |
1696 | { |
1697 | this->addExtension(spv::E_SPV_KHR_non_semantic_info); |
1698 | nonSemanticShaderDebugInfo = this->import("NonSemantic.Shader.DebugInfo.100" ); |
1699 | } |
1700 | |
1701 | return nonSemanticShaderDebugInfo; |
1702 | } |
1703 | |
1704 | Id Builder::findCompositeConstant(Op typeClass, Id typeId, const std::vector<Id>& comps) |
1705 | { |
1706 | Instruction* constant = nullptr; |
1707 | bool found = false; |
1708 | for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { |
1709 | constant = groupedConstants[typeClass][i]; |
1710 | |
1711 | if (constant->getTypeId() != typeId) |
1712 | continue; |
1713 | |
1714 | // same contents? |
1715 | bool mismatch = false; |
1716 | for (int op = 0; op < constant->getNumOperands(); ++op) { |
1717 | if (constant->getIdOperand(op) != comps[op]) { |
1718 | mismatch = true; |
1719 | break; |
1720 | } |
1721 | } |
1722 | if (! mismatch) { |
1723 | found = true; |
1724 | break; |
1725 | } |
1726 | } |
1727 | |
1728 | return found ? constant->getResultId() : NoResult; |
1729 | } |
1730 | |
1731 | Id Builder::findStructConstant(Id typeId, const std::vector<Id>& comps) |
1732 | { |
1733 | Instruction* constant = nullptr; |
1734 | bool found = false; |
1735 | for (int i = 0; i < (int)groupedStructConstants[typeId].size(); ++i) { |
1736 | constant = groupedStructConstants[typeId][i]; |
1737 | |
1738 | // same contents? |
1739 | bool mismatch = false; |
1740 | for (int op = 0; op < constant->getNumOperands(); ++op) { |
1741 | if (constant->getIdOperand(op) != comps[op]) { |
1742 | mismatch = true; |
1743 | break; |
1744 | } |
1745 | } |
1746 | if (! mismatch) { |
1747 | found = true; |
1748 | break; |
1749 | } |
1750 | } |
1751 | |
1752 | return found ? constant->getResultId() : NoResult; |
1753 | } |
1754 | |
1755 | // Comments in header |
1756 | Id Builder::makeCompositeConstant(Id typeId, const std::vector<Id>& members, bool specConstant) |
1757 | { |
1758 | Op opcode = specConstant ? OpSpecConstantComposite : OpConstantComposite; |
1759 | assert(typeId); |
1760 | Op typeClass = getTypeClass(typeId); |
1761 | |
1762 | switch (typeClass) { |
1763 | case OpTypeVector: |
1764 | case OpTypeArray: |
1765 | case OpTypeMatrix: |
1766 | case OpTypeCooperativeMatrixNV: |
1767 | if (! specConstant) { |
1768 | Id existing = findCompositeConstant(typeClass, typeId, members); |
1769 | if (existing) |
1770 | return existing; |
1771 | } |
1772 | break; |
1773 | case OpTypeStruct: |
1774 | if (! specConstant) { |
1775 | Id existing = findStructConstant(typeId, members); |
1776 | if (existing) |
1777 | return existing; |
1778 | } |
1779 | break; |
1780 | default: |
1781 | assert(0); |
1782 | return makeFloatConstant(0.0); |
1783 | } |
1784 | |
1785 | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
1786 | for (int op = 0; op < (int)members.size(); ++op) |
1787 | c->addIdOperand(members[op]); |
1788 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); |
1789 | if (typeClass == OpTypeStruct) |
1790 | groupedStructConstants[typeId].push_back(c); |
1791 | else |
1792 | groupedConstants[typeClass].push_back(c); |
1793 | module.mapInstruction(c); |
1794 | |
1795 | return c->getResultId(); |
1796 | } |
1797 | |
1798 | Instruction* Builder::addEntryPoint(ExecutionModel model, Function* function, const char* name) |
1799 | { |
1800 | Instruction* entryPoint = new Instruction(OpEntryPoint); |
1801 | entryPoint->addImmediateOperand(model); |
1802 | entryPoint->addIdOperand(function->getId()); |
1803 | entryPoint->addStringOperand(name); |
1804 | |
1805 | entryPoints.push_back(std::unique_ptr<Instruction>(entryPoint)); |
1806 | |
1807 | return entryPoint; |
1808 | } |
1809 | |
1810 | // Currently relying on the fact that all 'value' of interest are small non-negative values. |
1811 | void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, int value1, int value2, int value3) |
1812 | { |
1813 | Instruction* instr = new Instruction(OpExecutionMode); |
1814 | instr->addIdOperand(entryPoint->getId()); |
1815 | instr->addImmediateOperand(mode); |
1816 | if (value1 >= 0) |
1817 | instr->addImmediateOperand(value1); |
1818 | if (value2 >= 0) |
1819 | instr->addImmediateOperand(value2); |
1820 | if (value3 >= 0) |
1821 | instr->addImmediateOperand(value3); |
1822 | |
1823 | executionModes.push_back(std::unique_ptr<Instruction>(instr)); |
1824 | } |
1825 | |
1826 | void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, const std::vector<unsigned>& literals) |
1827 | { |
1828 | Instruction* instr = new Instruction(OpExecutionMode); |
1829 | instr->addIdOperand(entryPoint->getId()); |
1830 | instr->addImmediateOperand(mode); |
1831 | for (auto literal : literals) |
1832 | instr->addImmediateOperand(literal); |
1833 | |
1834 | executionModes.push_back(std::unique_ptr<Instruction>(instr)); |
1835 | } |
1836 | |
1837 | void Builder::addExecutionModeId(Function* entryPoint, ExecutionMode mode, const std::vector<Id>& operandIds) |
1838 | { |
1839 | Instruction* instr = new Instruction(OpExecutionModeId); |
1840 | instr->addIdOperand(entryPoint->getId()); |
1841 | instr->addImmediateOperand(mode); |
1842 | for (auto operandId : operandIds) |
1843 | instr->addIdOperand(operandId); |
1844 | |
1845 | executionModes.push_back(std::unique_ptr<Instruction>(instr)); |
1846 | } |
1847 | |
1848 | void Builder::addName(Id id, const char* string) |
1849 | { |
1850 | Instruction* name = new Instruction(OpName); |
1851 | name->addIdOperand(id); |
1852 | name->addStringOperand(string); |
1853 | |
1854 | names.push_back(std::unique_ptr<Instruction>(name)); |
1855 | } |
1856 | |
1857 | void Builder::addMemberName(Id id, int memberNumber, const char* string) |
1858 | { |
1859 | Instruction* name = new Instruction(OpMemberName); |
1860 | name->addIdOperand(id); |
1861 | name->addImmediateOperand(memberNumber); |
1862 | name->addStringOperand(string); |
1863 | |
1864 | names.push_back(std::unique_ptr<Instruction>(name)); |
1865 | } |
1866 | |
1867 | void Builder::addDecoration(Id id, Decoration decoration, int num) |
1868 | { |
1869 | if (decoration == spv::DecorationMax) |
1870 | return; |
1871 | |
1872 | Instruction* dec = new Instruction(OpDecorate); |
1873 | dec->addIdOperand(id); |
1874 | dec->addImmediateOperand(decoration); |
1875 | if (num >= 0) |
1876 | dec->addImmediateOperand(num); |
1877 | |
1878 | decorations.push_back(std::unique_ptr<Instruction>(dec)); |
1879 | } |
1880 | |
1881 | void Builder::addDecoration(Id id, Decoration decoration, const char* s) |
1882 | { |
1883 | if (decoration == spv::DecorationMax) |
1884 | return; |
1885 | |
1886 | Instruction* dec = new Instruction(OpDecorateString); |
1887 | dec->addIdOperand(id); |
1888 | dec->addImmediateOperand(decoration); |
1889 | dec->addStringOperand(s); |
1890 | |
1891 | decorations.push_back(std::unique_ptr<Instruction>(dec)); |
1892 | } |
1893 | |
1894 | void Builder::addDecoration(Id id, Decoration decoration, const std::vector<unsigned>& literals) |
1895 | { |
1896 | if (decoration == spv::DecorationMax) |
1897 | return; |
1898 | |
1899 | Instruction* dec = new Instruction(OpDecorate); |
1900 | dec->addIdOperand(id); |
1901 | dec->addImmediateOperand(decoration); |
1902 | for (auto literal : literals) |
1903 | dec->addImmediateOperand(literal); |
1904 | |
1905 | decorations.push_back(std::unique_ptr<Instruction>(dec)); |
1906 | } |
1907 | |
1908 | void Builder::addDecoration(Id id, Decoration decoration, const std::vector<const char*>& strings) |
1909 | { |
1910 | if (decoration == spv::DecorationMax) |
1911 | return; |
1912 | |
1913 | Instruction* dec = new Instruction(OpDecorateString); |
1914 | dec->addIdOperand(id); |
1915 | dec->addImmediateOperand(decoration); |
1916 | for (auto string : strings) |
1917 | dec->addStringOperand(string); |
1918 | |
1919 | decorations.push_back(std::unique_ptr<Instruction>(dec)); |
1920 | } |
1921 | |
1922 | void Builder::addDecorationId(Id id, Decoration decoration, Id idDecoration) |
1923 | { |
1924 | if (decoration == spv::DecorationMax) |
1925 | return; |
1926 | |
1927 | Instruction* dec = new Instruction(OpDecorateId); |
1928 | dec->addIdOperand(id); |
1929 | dec->addImmediateOperand(decoration); |
1930 | dec->addIdOperand(idDecoration); |
1931 | |
1932 | decorations.push_back(std::unique_ptr<Instruction>(dec)); |
1933 | } |
1934 | |
1935 | void Builder::addDecorationId(Id id, Decoration decoration, const std::vector<Id>& operandIds) |
1936 | { |
1937 | if(decoration == spv::DecorationMax) |
1938 | return; |
1939 | |
1940 | Instruction* dec = new Instruction(OpDecorateId); |
1941 | dec->addIdOperand(id); |
1942 | dec->addImmediateOperand(decoration); |
1943 | |
1944 | for (auto operandId : operandIds) |
1945 | dec->addIdOperand(operandId); |
1946 | |
1947 | decorations.push_back(std::unique_ptr<Instruction>(dec)); |
1948 | } |
1949 | |
1950 | void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, int num) |
1951 | { |
1952 | if (decoration == spv::DecorationMax) |
1953 | return; |
1954 | |
1955 | Instruction* dec = new Instruction(OpMemberDecorate); |
1956 | dec->addIdOperand(id); |
1957 | dec->addImmediateOperand(member); |
1958 | dec->addImmediateOperand(decoration); |
1959 | if (num >= 0) |
1960 | dec->addImmediateOperand(num); |
1961 | |
1962 | decorations.push_back(std::unique_ptr<Instruction>(dec)); |
1963 | } |
1964 | |
1965 | void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const char *s) |
1966 | { |
1967 | if (decoration == spv::DecorationMax) |
1968 | return; |
1969 | |
1970 | Instruction* dec = new Instruction(OpMemberDecorateStringGOOGLE); |
1971 | dec->addIdOperand(id); |
1972 | dec->addImmediateOperand(member); |
1973 | dec->addImmediateOperand(decoration); |
1974 | dec->addStringOperand(s); |
1975 | |
1976 | decorations.push_back(std::unique_ptr<Instruction>(dec)); |
1977 | } |
1978 | |
1979 | void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector<unsigned>& literals) |
1980 | { |
1981 | if (decoration == spv::DecorationMax) |
1982 | return; |
1983 | |
1984 | Instruction* dec = new Instruction(OpMemberDecorate); |
1985 | dec->addIdOperand(id); |
1986 | dec->addImmediateOperand(member); |
1987 | dec->addImmediateOperand(decoration); |
1988 | for (auto literal : literals) |
1989 | dec->addImmediateOperand(literal); |
1990 | |
1991 | decorations.push_back(std::unique_ptr<Instruction>(dec)); |
1992 | } |
1993 | |
1994 | void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector<const char*>& strings) |
1995 | { |
1996 | if (decoration == spv::DecorationMax) |
1997 | return; |
1998 | |
1999 | Instruction* dec = new Instruction(OpMemberDecorateString); |
2000 | dec->addIdOperand(id); |
2001 | dec->addImmediateOperand(member); |
2002 | dec->addImmediateOperand(decoration); |
2003 | for (auto string : strings) |
2004 | dec->addStringOperand(string); |
2005 | |
2006 | decorations.push_back(std::unique_ptr<Instruction>(dec)); |
2007 | } |
2008 | |
2009 | // Comments in header |
2010 | Function* Builder::makeEntryPoint(const char* entryPoint) |
2011 | { |
2012 | assert(! entryPointFunction); |
2013 | |
2014 | Block* entry; |
2015 | std::vector<Id> paramsTypes; |
2016 | std::vector<char const*> paramNames; |
2017 | std::vector<std::vector<Decoration>> decorations; |
2018 | |
2019 | auto const returnType = makeVoidType(); |
2020 | |
2021 | restoreNonSemanticShaderDebugInfo = emitNonSemanticShaderDebugInfo; |
2022 | if(sourceLang == spv::SourceLanguageHLSL) { |
2023 | emitNonSemanticShaderDebugInfo = false; |
2024 | } |
2025 | |
2026 | entryPointFunction = makeFunctionEntry(NoPrecision, returnType, entryPoint, paramsTypes, paramNames, decorations, &entry); |
2027 | |
2028 | emitNonSemanticShaderDebugInfo = restoreNonSemanticShaderDebugInfo; |
2029 | |
2030 | return entryPointFunction; |
2031 | } |
2032 | |
2033 | // Comments in header |
2034 | Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const char* name, |
2035 | const std::vector<Id>& paramTypes, const std::vector<char const*>& paramNames, |
2036 | const std::vector<std::vector<Decoration>>& decorations, Block **entry) |
2037 | { |
2038 | // Make the function and initial instructions in it |
2039 | Id typeId = makeFunctionType(returnType, paramTypes); |
2040 | Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds((int)paramTypes.size()); |
2041 | Id funcId = getUniqueId(); |
2042 | Function* function = new Function(funcId, returnType, typeId, firstParamId, module); |
2043 | |
2044 | // Set up the precisions |
2045 | setPrecision(function->getId(), precision); |
2046 | function->setReturnPrecision(precision); |
2047 | for (unsigned p = 0; p < (unsigned)decorations.size(); ++p) { |
2048 | for (int d = 0; d < (int)decorations[p].size(); ++d) { |
2049 | addDecoration(firstParamId + p, decorations[p][d]); |
2050 | function->addParamPrecision(p, decorations[p][d]); |
2051 | } |
2052 | } |
2053 | |
2054 | // Make the debug function instruction |
2055 | if (emitNonSemanticShaderDebugInfo) { |
2056 | Id nameId = getStringId(unmangleFunctionName(name)); |
2057 | Id debugFuncId = makeDebugFunction(function, nameId, typeId); |
2058 | debugId[funcId] = debugFuncId; |
2059 | currentDebugScopeId.push(debugFuncId); |
2060 | lastDebugScopeId = NoResult; |
2061 | } |
2062 | |
2063 | // CFG |
2064 | assert(entry != nullptr); |
2065 | *entry = new Block(getUniqueId(), *function); |
2066 | function->addBlock(*entry); |
2067 | setBuildPoint(*entry); |
2068 | |
2069 | // DebugScope and DebugLine for parameter DebugDeclares |
2070 | if (emitNonSemanticShaderDebugInfo && (int)paramTypes.size() > 0) { |
2071 | addDebugScopeAndLine(currentFileId, currentLine, 0); |
2072 | } |
2073 | |
2074 | if (emitNonSemanticShaderDebugInfo) { |
2075 | assert(paramTypes.size() == paramNames.size()); |
2076 | for(size_t p = 0; p < paramTypes.size(); ++p) |
2077 | { |
2078 | auto getParamTypeId = [this](Id const& typeId) { |
2079 | if (isPointerType(typeId) || isArrayType(typeId)) { |
2080 | return getContainedTypeId(typeId); |
2081 | } |
2082 | else { |
2083 | return typeId; |
2084 | } |
2085 | }; |
2086 | auto const& paramName = paramNames[p]; |
2087 | auto const debugLocalVariableId = createDebugLocalVariable(debugId[getParamTypeId(paramTypes[p])], paramName, p+1); |
2088 | debugId[firstParamId + p] = debugLocalVariableId; |
2089 | |
2090 | makeDebugDeclare(debugLocalVariableId, firstParamId + p); |
2091 | } |
2092 | } |
2093 | |
2094 | if (name) |
2095 | addName(function->getId(), name); |
2096 | |
2097 | functions.push_back(std::unique_ptr<Function>(function)); |
2098 | |
2099 | // Clear debug scope stack |
2100 | if (emitNonSemanticShaderDebugInfo) |
2101 | currentDebugScopeId.pop(); |
2102 | |
2103 | return function; |
2104 | } |
2105 | |
2106 | Id Builder::makeDebugFunction(Function* function, Id nameId, Id funcTypeId) { |
2107 | assert(function != nullptr); |
2108 | assert(nameId != 0); |
2109 | assert(funcTypeId != 0); |
2110 | assert(debugId[funcTypeId] != 0); |
2111 | |
2112 | Id funcId = getUniqueId(); |
2113 | auto type = new Instruction(funcId, makeVoidType(), OpExtInst); |
2114 | type->addIdOperand(nonSemanticShaderDebugInfo); |
2115 | type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugFunction); |
2116 | type->addIdOperand(nameId); |
2117 | type->addIdOperand(debugId[funcTypeId]); |
2118 | type->addIdOperand(makeDebugSource(currentFileId)); // Will be fixed later when true filename available |
2119 | type->addIdOperand(makeUintConstant(currentLine)); // Will be fixed later when true line available |
2120 | type->addIdOperand(makeUintConstant(0)); // column |
2121 | type->addIdOperand(makeDebugCompilationUnit()); // scope |
2122 | type->addIdOperand(nameId); // linkage name |
2123 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic)); |
2124 | type->addIdOperand(makeUintConstant(currentLine)); // TODO(greg-lunarg): correct scope line |
2125 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
2126 | module.mapInstruction(type); |
2127 | return funcId; |
2128 | } |
2129 | |
2130 | Id Builder::makeDebugLexicalBlock(uint32_t line) { |
2131 | assert(!currentDebugScopeId.empty()); |
2132 | |
2133 | Id lexId = getUniqueId(); |
2134 | auto lex = new Instruction(lexId, makeVoidType(), OpExtInst); |
2135 | lex->addIdOperand(nonSemanticShaderDebugInfo); |
2136 | lex->addImmediateOperand(NonSemanticShaderDebugInfo100DebugLexicalBlock); |
2137 | lex->addIdOperand(makeDebugSource(currentFileId)); |
2138 | lex->addIdOperand(makeUintConstant(line)); |
2139 | lex->addIdOperand(makeUintConstant(0)); // column |
2140 | lex->addIdOperand(currentDebugScopeId.top()); // scope |
2141 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(lex)); |
2142 | module.mapInstruction(lex); |
2143 | return lexId; |
2144 | } |
2145 | |
2146 | std::string Builder::unmangleFunctionName(std::string const& name) const |
2147 | { |
2148 | assert(name.length() > 0); |
2149 | |
2150 | if(name.rfind('(') != std::string::npos) { |
2151 | return name.substr(0, name.rfind('(')); |
2152 | } else { |
2153 | return name; |
2154 | } |
2155 | } |
2156 | |
2157 | // Comments in header |
2158 | void Builder::makeReturn(bool implicit, Id retVal) |
2159 | { |
2160 | if (retVal) { |
2161 | Instruction* inst = new Instruction(NoResult, NoType, OpReturnValue); |
2162 | inst->addIdOperand(retVal); |
2163 | buildPoint->addInstruction(std::unique_ptr<Instruction>(inst)); |
2164 | } else |
2165 | buildPoint->addInstruction(std::unique_ptr<Instruction>(new Instruction(NoResult, NoType, OpReturn))); |
2166 | |
2167 | if (! implicit) |
2168 | createAndSetNoPredecessorBlock("post-return" ); |
2169 | } |
2170 | |
2171 | // Comments in header |
2172 | void Builder::enterScope(uint32_t line) |
2173 | { |
2174 | // Generate new lexical scope debug instruction |
2175 | Id lexId = makeDebugLexicalBlock(line); |
2176 | currentDebugScopeId.push(lexId); |
2177 | lastDebugScopeId = NoResult; |
2178 | } |
2179 | |
2180 | // Comments in header |
2181 | void Builder::leaveScope() |
2182 | { |
2183 | // Pop current scope from stack and clear current scope |
2184 | currentDebugScopeId.pop(); |
2185 | lastDebugScopeId = NoResult; |
2186 | } |
2187 | |
2188 | // Comments in header |
2189 | void Builder::enterFunction(Function const* function) |
2190 | { |
2191 | // Save and disable debugInfo for HLSL entry point function. It is a wrapper |
2192 | // function with no user code in it. |
2193 | restoreNonSemanticShaderDebugInfo = emitNonSemanticShaderDebugInfo; |
2194 | if (sourceLang == spv::SourceLanguageHLSL && function == entryPointFunction) { |
2195 | emitNonSemanticShaderDebugInfo = false; |
2196 | } |
2197 | |
2198 | if (emitNonSemanticShaderDebugInfo) { |
2199 | // Initialize scope state |
2200 | Id funcId = function->getFuncId(); |
2201 | currentDebugScopeId.push(debugId[funcId]); |
2202 | // Create DebugFunctionDefinition |
2203 | spv::Id resultId = getUniqueId(); |
2204 | Instruction* defInst = new Instruction(resultId, makeVoidType(), OpExtInst); |
2205 | defInst->addIdOperand(nonSemanticShaderDebugInfo); |
2206 | defInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugFunctionDefinition); |
2207 | defInst->addIdOperand(debugId[funcId]); |
2208 | defInst->addIdOperand(funcId); |
2209 | buildPoint->addInstruction(std::unique_ptr<Instruction>(defInst)); |
2210 | } |
2211 | } |
2212 | |
2213 | // Comments in header |
2214 | void Builder::leaveFunction() |
2215 | { |
2216 | Block* block = buildPoint; |
2217 | Function& function = buildPoint->getParent(); |
2218 | assert(block); |
2219 | |
2220 | // If our function did not contain a return, add a return void now. |
2221 | if (! block->isTerminated()) { |
2222 | if (function.getReturnType() == makeVoidType()) |
2223 | makeReturn(true); |
2224 | else { |
2225 | makeReturn(true, createUndefined(function.getReturnType())); |
2226 | } |
2227 | } |
2228 | |
2229 | // Clear function scope from debug scope stack |
2230 | if (emitNonSemanticShaderDebugInfo) |
2231 | currentDebugScopeId.pop(); |
2232 | |
2233 | emitNonSemanticShaderDebugInfo = restoreNonSemanticShaderDebugInfo; |
2234 | } |
2235 | |
2236 | // Comments in header |
2237 | void Builder::makeStatementTerminator(spv::Op opcode, const char *name) |
2238 | { |
2239 | buildPoint->addInstruction(std::unique_ptr<Instruction>(new Instruction(opcode))); |
2240 | createAndSetNoPredecessorBlock(name); |
2241 | } |
2242 | |
2243 | // Comments in header |
2244 | void Builder::makeStatementTerminator(spv::Op opcode, const std::vector<Id>& operands, const char* name) |
2245 | { |
2246 | // It's assumed that the terminator instruction is always of void return type |
2247 | // However in future if there is a need for non void return type, new helper |
2248 | // methods can be created. |
2249 | createNoResultOp(opcode, operands); |
2250 | createAndSetNoPredecessorBlock(name); |
2251 | } |
2252 | |
2253 | // Comments in header |
2254 | Id Builder::createVariable(Decoration precision, StorageClass storageClass, Id type, const char* name, Id initializer, |
2255 | bool const compilerGenerated) |
2256 | { |
2257 | Id pointerType = makePointer(storageClass, type); |
2258 | Instruction* inst = new Instruction(getUniqueId(), pointerType, OpVariable); |
2259 | inst->addImmediateOperand(storageClass); |
2260 | if (initializer != NoResult) |
2261 | inst->addIdOperand(initializer); |
2262 | |
2263 | switch (storageClass) { |
2264 | case StorageClassFunction: |
2265 | // Validation rules require the declaration in the entry block |
2266 | buildPoint->getParent().addLocalVariable(std::unique_ptr<Instruction>(inst)); |
2267 | |
2268 | if (emitNonSemanticShaderDebugInfo && !compilerGenerated) |
2269 | { |
2270 | auto const debugLocalVariableId = createDebugLocalVariable(debugId[type], name); |
2271 | debugId[inst->getResultId()] = debugLocalVariableId; |
2272 | |
2273 | makeDebugDeclare(debugLocalVariableId, inst->getResultId()); |
2274 | } |
2275 | |
2276 | break; |
2277 | |
2278 | default: |
2279 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst)); |
2280 | module.mapInstruction(inst); |
2281 | |
2282 | if (emitNonSemanticShaderDebugInfo && !isRayTracingOpCode(getOpCode(type))) |
2283 | { |
2284 | auto const debugResultId = createDebugGlobalVariable(debugId[type], name, inst->getResultId()); |
2285 | debugId[inst->getResultId()] = debugResultId; |
2286 | } |
2287 | break; |
2288 | } |
2289 | |
2290 | if (name) |
2291 | addName(inst->getResultId(), name); |
2292 | setPrecision(inst->getResultId(), precision); |
2293 | |
2294 | return inst->getResultId(); |
2295 | } |
2296 | |
2297 | // Comments in header |
2298 | Id Builder::createUndefined(Id type) |
2299 | { |
2300 | Instruction* inst = new Instruction(getUniqueId(), type, OpUndef); |
2301 | buildPoint->addInstruction(std::unique_ptr<Instruction>(inst)); |
2302 | return inst->getResultId(); |
2303 | } |
2304 | |
2305 | // av/vis/nonprivate are unnecessary and illegal for some storage classes. |
2306 | spv::MemoryAccessMask Builder::sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc) |
2307 | const |
2308 | { |
2309 | switch (sc) { |
2310 | case spv::StorageClassUniform: |
2311 | case spv::StorageClassWorkgroup: |
2312 | case spv::StorageClassStorageBuffer: |
2313 | case spv::StorageClassPhysicalStorageBufferEXT: |
2314 | break; |
2315 | default: |
2316 | memoryAccess = spv::MemoryAccessMask(memoryAccess & |
2317 | ~(spv::MemoryAccessMakePointerAvailableKHRMask | |
2318 | spv::MemoryAccessMakePointerVisibleKHRMask | |
2319 | spv::MemoryAccessNonPrivatePointerKHRMask)); |
2320 | break; |
2321 | } |
2322 | return memoryAccess; |
2323 | } |
2324 | |
2325 | // Comments in header |
2326 | void Builder::createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope, |
2327 | unsigned int alignment) |
2328 | { |
2329 | Instruction* store = new Instruction(OpStore); |
2330 | store->addIdOperand(lValue); |
2331 | store->addIdOperand(rValue); |
2332 | |
2333 | memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue)); |
2334 | |
2335 | if (memoryAccess != MemoryAccessMaskNone) { |
2336 | store->addImmediateOperand(memoryAccess); |
2337 | if (memoryAccess & spv::MemoryAccessAlignedMask) { |
2338 | store->addImmediateOperand(alignment); |
2339 | } |
2340 | if (memoryAccess & spv::MemoryAccessMakePointerAvailableKHRMask) { |
2341 | store->addIdOperand(makeUintConstant(scope)); |
2342 | } |
2343 | } |
2344 | |
2345 | buildPoint->addInstruction(std::unique_ptr<Instruction>(store)); |
2346 | } |
2347 | |
2348 | // Comments in header |
2349 | Id Builder::createLoad(Id lValue, spv::Decoration precision, spv::MemoryAccessMask memoryAccess, |
2350 | spv::Scope scope, unsigned int alignment) |
2351 | { |
2352 | Instruction* load = new Instruction(getUniqueId(), getDerefTypeId(lValue), OpLoad); |
2353 | load->addIdOperand(lValue); |
2354 | |
2355 | memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue)); |
2356 | |
2357 | if (memoryAccess != MemoryAccessMaskNone) { |
2358 | load->addImmediateOperand(memoryAccess); |
2359 | if (memoryAccess & spv::MemoryAccessAlignedMask) { |
2360 | load->addImmediateOperand(alignment); |
2361 | } |
2362 | if (memoryAccess & spv::MemoryAccessMakePointerVisibleKHRMask) { |
2363 | load->addIdOperand(makeUintConstant(scope)); |
2364 | } |
2365 | } |
2366 | |
2367 | buildPoint->addInstruction(std::unique_ptr<Instruction>(load)); |
2368 | setPrecision(load->getResultId(), precision); |
2369 | |
2370 | return load->getResultId(); |
2371 | } |
2372 | |
2373 | // Comments in header |
2374 | Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vector<Id>& offsets) |
2375 | { |
2376 | // Figure out the final resulting type. |
2377 | Id typeId = getResultingAccessChainType(); |
2378 | typeId = makePointer(storageClass, typeId); |
2379 | |
2380 | // Make the instruction |
2381 | Instruction* chain = new Instruction(getUniqueId(), typeId, OpAccessChain); |
2382 | chain->addIdOperand(base); |
2383 | for (int i = 0; i < (int)offsets.size(); ++i) |
2384 | chain->addIdOperand(offsets[i]); |
2385 | buildPoint->addInstruction(std::unique_ptr<Instruction>(chain)); |
2386 | |
2387 | return chain->getResultId(); |
2388 | } |
2389 | |
2390 | Id Builder::createArrayLength(Id base, unsigned int member) |
2391 | { |
2392 | spv::Id intType = makeUintType(32); |
2393 | Instruction* length = new Instruction(getUniqueId(), intType, OpArrayLength); |
2394 | length->addIdOperand(base); |
2395 | length->addImmediateOperand(member); |
2396 | buildPoint->addInstruction(std::unique_ptr<Instruction>(length)); |
2397 | |
2398 | return length->getResultId(); |
2399 | } |
2400 | |
2401 | Id Builder::createCooperativeMatrixLength(Id type) |
2402 | { |
2403 | spv::Id intType = makeUintType(32); |
2404 | |
2405 | // Generate code for spec constants if in spec constant operation |
2406 | // generation mode. |
2407 | if (generatingOpCodeForSpecConst) { |
2408 | return createSpecConstantOp(OpCooperativeMatrixLengthNV, intType, std::vector<Id>(1, type), std::vector<Id>()); |
2409 | } |
2410 | |
2411 | Instruction* length = new Instruction(getUniqueId(), intType, OpCooperativeMatrixLengthNV); |
2412 | length->addIdOperand(type); |
2413 | buildPoint->addInstruction(std::unique_ptr<Instruction>(length)); |
2414 | |
2415 | return length->getResultId(); |
2416 | } |
2417 | |
2418 | Id Builder::(Id composite, Id typeId, unsigned index) |
2419 | { |
2420 | // Generate code for spec constants if in spec constant operation |
2421 | // generation mode. |
2422 | if (generatingOpCodeForSpecConst) { |
2423 | return createSpecConstantOp(OpCompositeExtract, typeId, std::vector<Id>(1, composite), |
2424 | std::vector<Id>(1, index)); |
2425 | } |
2426 | Instruction* = new Instruction(getUniqueId(), typeId, OpCompositeExtract); |
2427 | extract->addIdOperand(composite); |
2428 | extract->addImmediateOperand(index); |
2429 | buildPoint->addInstruction(std::unique_ptr<Instruction>(extract)); |
2430 | |
2431 | return extract->getResultId(); |
2432 | } |
2433 | |
2434 | Id Builder::(Id composite, Id typeId, const std::vector<unsigned>& indexes) |
2435 | { |
2436 | // Generate code for spec constants if in spec constant operation |
2437 | // generation mode. |
2438 | if (generatingOpCodeForSpecConst) { |
2439 | return createSpecConstantOp(OpCompositeExtract, typeId, std::vector<Id>(1, composite), indexes); |
2440 | } |
2441 | Instruction* = new Instruction(getUniqueId(), typeId, OpCompositeExtract); |
2442 | extract->addIdOperand(composite); |
2443 | for (int i = 0; i < (int)indexes.size(); ++i) |
2444 | extract->addImmediateOperand(indexes[i]); |
2445 | buildPoint->addInstruction(std::unique_ptr<Instruction>(extract)); |
2446 | |
2447 | return extract->getResultId(); |
2448 | } |
2449 | |
2450 | Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, unsigned index) |
2451 | { |
2452 | Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert); |
2453 | insert->addIdOperand(object); |
2454 | insert->addIdOperand(composite); |
2455 | insert->addImmediateOperand(index); |
2456 | buildPoint->addInstruction(std::unique_ptr<Instruction>(insert)); |
2457 | |
2458 | return insert->getResultId(); |
2459 | } |
2460 | |
2461 | Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes) |
2462 | { |
2463 | Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert); |
2464 | insert->addIdOperand(object); |
2465 | insert->addIdOperand(composite); |
2466 | for (int i = 0; i < (int)indexes.size(); ++i) |
2467 | insert->addImmediateOperand(indexes[i]); |
2468 | buildPoint->addInstruction(std::unique_ptr<Instruction>(insert)); |
2469 | |
2470 | return insert->getResultId(); |
2471 | } |
2472 | |
2473 | Id Builder::(Id vector, Id typeId, Id componentIndex) |
2474 | { |
2475 | Instruction* = new Instruction(getUniqueId(), typeId, OpVectorExtractDynamic); |
2476 | extract->addIdOperand(vector); |
2477 | extract->addIdOperand(componentIndex); |
2478 | buildPoint->addInstruction(std::unique_ptr<Instruction>(extract)); |
2479 | |
2480 | return extract->getResultId(); |
2481 | } |
2482 | |
2483 | Id Builder::createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex) |
2484 | { |
2485 | Instruction* insert = new Instruction(getUniqueId(), typeId, OpVectorInsertDynamic); |
2486 | insert->addIdOperand(vector); |
2487 | insert->addIdOperand(component); |
2488 | insert->addIdOperand(componentIndex); |
2489 | buildPoint->addInstruction(std::unique_ptr<Instruction>(insert)); |
2490 | |
2491 | return insert->getResultId(); |
2492 | } |
2493 | |
2494 | // An opcode that has no operands, no result id, and no type |
2495 | void Builder::createNoResultOp(Op opCode) |
2496 | { |
2497 | Instruction* op = new Instruction(opCode); |
2498 | buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); |
2499 | } |
2500 | |
2501 | // An opcode that has one id operand, no result id, and no type |
2502 | void Builder::createNoResultOp(Op opCode, Id operand) |
2503 | { |
2504 | Instruction* op = new Instruction(opCode); |
2505 | op->addIdOperand(operand); |
2506 | buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); |
2507 | } |
2508 | |
2509 | // An opcode that has one or more operands, no result id, and no type |
2510 | void Builder::createNoResultOp(Op opCode, const std::vector<Id>& operands) |
2511 | { |
2512 | Instruction* op = new Instruction(opCode); |
2513 | for (auto it = operands.cbegin(); it != operands.cend(); ++it) { |
2514 | op->addIdOperand(*it); |
2515 | } |
2516 | buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); |
2517 | } |
2518 | |
2519 | // An opcode that has multiple operands, no result id, and no type |
2520 | void Builder::createNoResultOp(Op opCode, const std::vector<IdImmediate>& operands) |
2521 | { |
2522 | Instruction* op = new Instruction(opCode); |
2523 | for (auto it = operands.cbegin(); it != operands.cend(); ++it) { |
2524 | if (it->isId) |
2525 | op->addIdOperand(it->word); |
2526 | else |
2527 | op->addImmediateOperand(it->word); |
2528 | } |
2529 | buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); |
2530 | } |
2531 | |
2532 | void Builder::createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask semantics) |
2533 | { |
2534 | Instruction* op = new Instruction(OpControlBarrier); |
2535 | op->addIdOperand(makeUintConstant(execution)); |
2536 | op->addIdOperand(makeUintConstant(memory)); |
2537 | op->addIdOperand(makeUintConstant(semantics)); |
2538 | buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); |
2539 | } |
2540 | |
2541 | void Builder::createMemoryBarrier(unsigned executionScope, unsigned memorySemantics) |
2542 | { |
2543 | Instruction* op = new Instruction(OpMemoryBarrier); |
2544 | op->addIdOperand(makeUintConstant(executionScope)); |
2545 | op->addIdOperand(makeUintConstant(memorySemantics)); |
2546 | buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); |
2547 | } |
2548 | |
2549 | // An opcode that has one operands, a result id, and a type |
2550 | Id Builder::createUnaryOp(Op opCode, Id typeId, Id operand) |
2551 | { |
2552 | // Generate code for spec constants if in spec constant operation |
2553 | // generation mode. |
2554 | if (generatingOpCodeForSpecConst) { |
2555 | return createSpecConstantOp(opCode, typeId, std::vector<Id>(1, operand), std::vector<Id>()); |
2556 | } |
2557 | Instruction* op = new Instruction(getUniqueId(), typeId, opCode); |
2558 | op->addIdOperand(operand); |
2559 | buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); |
2560 | |
2561 | return op->getResultId(); |
2562 | } |
2563 | |
2564 | Id Builder::createBinOp(Op opCode, Id typeId, Id left, Id right) |
2565 | { |
2566 | // Generate code for spec constants if in spec constant operation |
2567 | // generation mode. |
2568 | if (generatingOpCodeForSpecConst) { |
2569 | std::vector<Id> operands(2); |
2570 | operands[0] = left; operands[1] = right; |
2571 | return createSpecConstantOp(opCode, typeId, operands, std::vector<Id>()); |
2572 | } |
2573 | Instruction* op = new Instruction(getUniqueId(), typeId, opCode); |
2574 | op->addIdOperand(left); |
2575 | op->addIdOperand(right); |
2576 | buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); |
2577 | |
2578 | return op->getResultId(); |
2579 | } |
2580 | |
2581 | Id Builder::createTriOp(Op opCode, Id typeId, Id op1, Id op2, Id op3) |
2582 | { |
2583 | // Generate code for spec constants if in spec constant operation |
2584 | // generation mode. |
2585 | if (generatingOpCodeForSpecConst) { |
2586 | std::vector<Id> operands(3); |
2587 | operands[0] = op1; |
2588 | operands[1] = op2; |
2589 | operands[2] = op3; |
2590 | return createSpecConstantOp( |
2591 | opCode, typeId, operands, std::vector<Id>()); |
2592 | } |
2593 | Instruction* op = new Instruction(getUniqueId(), typeId, opCode); |
2594 | op->addIdOperand(op1); |
2595 | op->addIdOperand(op2); |
2596 | op->addIdOperand(op3); |
2597 | buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); |
2598 | |
2599 | return op->getResultId(); |
2600 | } |
2601 | |
2602 | Id Builder::createOp(Op opCode, Id typeId, const std::vector<Id>& operands) |
2603 | { |
2604 | Instruction* op = new Instruction(getUniqueId(), typeId, opCode); |
2605 | for (auto it = operands.cbegin(); it != operands.cend(); ++it) |
2606 | op->addIdOperand(*it); |
2607 | buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); |
2608 | |
2609 | return op->getResultId(); |
2610 | } |
2611 | |
2612 | Id Builder::createOp(Op opCode, Id typeId, const std::vector<IdImmediate>& operands) |
2613 | { |
2614 | Instruction* op = new Instruction(getUniqueId(), typeId, opCode); |
2615 | for (auto it = operands.cbegin(); it != operands.cend(); ++it) { |
2616 | if (it->isId) |
2617 | op->addIdOperand(it->word); |
2618 | else |
2619 | op->addImmediateOperand(it->word); |
2620 | } |
2621 | buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); |
2622 | |
2623 | return op->getResultId(); |
2624 | } |
2625 | |
2626 | Id Builder::createSpecConstantOp(Op opCode, Id typeId, const std::vector<Id>& operands, |
2627 | const std::vector<unsigned>& literals) |
2628 | { |
2629 | Instruction* op = new Instruction(getUniqueId(), typeId, OpSpecConstantOp); |
2630 | op->addImmediateOperand((unsigned) opCode); |
2631 | for (auto it = operands.cbegin(); it != operands.cend(); ++it) |
2632 | op->addIdOperand(*it); |
2633 | for (auto it = literals.cbegin(); it != literals.cend(); ++it) |
2634 | op->addImmediateOperand(*it); |
2635 | module.mapInstruction(op); |
2636 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(op)); |
2637 | |
2638 | return op->getResultId(); |
2639 | } |
2640 | |
2641 | Id Builder::createFunctionCall(spv::Function* function, const std::vector<spv::Id>& args) |
2642 | { |
2643 | Instruction* op = new Instruction(getUniqueId(), function->getReturnType(), OpFunctionCall); |
2644 | op->addIdOperand(function->getId()); |
2645 | for (int a = 0; a < (int)args.size(); ++a) |
2646 | op->addIdOperand(args[a]); |
2647 | buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); |
2648 | |
2649 | return op->getResultId(); |
2650 | } |
2651 | |
2652 | // Comments in header |
2653 | Id Builder::createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels) |
2654 | { |
2655 | if (channels.size() == 1) |
2656 | return setPrecision(createCompositeExtract(source, typeId, channels.front()), precision); |
2657 | |
2658 | if (generatingOpCodeForSpecConst) { |
2659 | std::vector<Id> operands(2); |
2660 | operands[0] = operands[1] = source; |
2661 | return setPrecision(createSpecConstantOp(OpVectorShuffle, typeId, operands, channels), precision); |
2662 | } |
2663 | Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle); |
2664 | assert(isVector(source)); |
2665 | swizzle->addIdOperand(source); |
2666 | swizzle->addIdOperand(source); |
2667 | for (int i = 0; i < (int)channels.size(); ++i) |
2668 | swizzle->addImmediateOperand(channels[i]); |
2669 | buildPoint->addInstruction(std::unique_ptr<Instruction>(swizzle)); |
2670 | |
2671 | return setPrecision(swizzle->getResultId(), precision); |
2672 | } |
2673 | |
2674 | // Comments in header |
2675 | Id Builder::createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels) |
2676 | { |
2677 | if (channels.size() == 1 && getNumComponents(source) == 1) |
2678 | return createCompositeInsert(source, target, typeId, channels.front()); |
2679 | |
2680 | Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle); |
2681 | |
2682 | assert(isVector(target)); |
2683 | swizzle->addIdOperand(target); |
2684 | |
2685 | assert(getNumComponents(source) == (int)channels.size()); |
2686 | assert(isVector(source)); |
2687 | swizzle->addIdOperand(source); |
2688 | |
2689 | // Set up an identity shuffle from the base value to the result value |
2690 | unsigned int components[4]; |
2691 | int numTargetComponents = getNumComponents(target); |
2692 | for (int i = 0; i < numTargetComponents; ++i) |
2693 | components[i] = i; |
2694 | |
2695 | // Punch in the l-value swizzle |
2696 | for (int i = 0; i < (int)channels.size(); ++i) |
2697 | components[channels[i]] = numTargetComponents + i; |
2698 | |
2699 | // finish the instruction with these components selectors |
2700 | for (int i = 0; i < numTargetComponents; ++i) |
2701 | swizzle->addImmediateOperand(components[i]); |
2702 | buildPoint->addInstruction(std::unique_ptr<Instruction>(swizzle)); |
2703 | |
2704 | return swizzle->getResultId(); |
2705 | } |
2706 | |
2707 | // Comments in header |
2708 | void Builder::promoteScalar(Decoration precision, Id& left, Id& right) |
2709 | { |
2710 | int direction = getNumComponents(right) - getNumComponents(left); |
2711 | |
2712 | if (direction > 0) |
2713 | left = smearScalar(precision, left, makeVectorType(getTypeId(left), getNumComponents(right))); |
2714 | else if (direction < 0) |
2715 | right = smearScalar(precision, right, makeVectorType(getTypeId(right), getNumComponents(left))); |
2716 | |
2717 | return; |
2718 | } |
2719 | |
2720 | // Comments in header |
2721 | Id Builder::smearScalar(Decoration precision, Id scalar, Id vectorType) |
2722 | { |
2723 | assert(getNumComponents(scalar) == 1); |
2724 | assert(getTypeId(scalar) == getScalarTypeId(vectorType)); |
2725 | |
2726 | int numComponents = getNumTypeComponents(vectorType); |
2727 | if (numComponents == 1) |
2728 | return scalar; |
2729 | |
2730 | Instruction* smear = nullptr; |
2731 | if (generatingOpCodeForSpecConst) { |
2732 | auto members = std::vector<spv::Id>(numComponents, scalar); |
2733 | // Sometime even in spec-constant-op mode, the temporary vector created by |
2734 | // promoting a scalar might not be a spec constant. This should depend on |
2735 | // the scalar. |
2736 | // e.g.: |
2737 | // const vec2 spec_const_result = a_spec_const_vec2 + a_front_end_const_scalar; |
2738 | // In such cases, the temporary vector created from a_front_end_const_scalar |
2739 | // is not a spec constant vector, even though the binary operation node is marked |
2740 | // as 'specConstant' and we are in spec-constant-op mode. |
2741 | auto result_id = makeCompositeConstant(vectorType, members, isSpecConstant(scalar)); |
2742 | smear = module.getInstruction(result_id); |
2743 | } else { |
2744 | smear = new Instruction(getUniqueId(), vectorType, OpCompositeConstruct); |
2745 | for (int c = 0; c < numComponents; ++c) |
2746 | smear->addIdOperand(scalar); |
2747 | buildPoint->addInstruction(std::unique_ptr<Instruction>(smear)); |
2748 | } |
2749 | |
2750 | return setPrecision(smear->getResultId(), precision); |
2751 | } |
2752 | |
2753 | // Comments in header |
2754 | Id Builder::createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args) |
2755 | { |
2756 | Instruction* inst = new Instruction(getUniqueId(), resultType, OpExtInst); |
2757 | inst->addIdOperand(builtins); |
2758 | inst->addImmediateOperand(entryPoint); |
2759 | for (int arg = 0; arg < (int)args.size(); ++arg) |
2760 | inst->addIdOperand(args[arg]); |
2761 | |
2762 | buildPoint->addInstruction(std::unique_ptr<Instruction>(inst)); |
2763 | |
2764 | return inst->getResultId(); |
2765 | } |
2766 | |
2767 | // Accept all parameters needed to create a texture instruction. |
2768 | // Create the correct instruction based on the inputs, and make the call. |
2769 | Id Builder::createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather, |
2770 | bool noImplicitLod, const TextureParameters& parameters, ImageOperandsMask signExtensionMask) |
2771 | { |
2772 | std::vector<Id> texArgs; |
2773 | |
2774 | // |
2775 | // Set up the fixed arguments |
2776 | // |
2777 | bool explicitLod = false; |
2778 | texArgs.push_back(parameters.sampler); |
2779 | texArgs.push_back(parameters.coords); |
2780 | if (parameters.Dref != NoResult) |
2781 | texArgs.push_back(parameters.Dref); |
2782 | if (parameters.component != NoResult) |
2783 | texArgs.push_back(parameters.component); |
2784 | |
2785 | #ifndef GLSLANG_WEB |
2786 | if (parameters.granularity != NoResult) |
2787 | texArgs.push_back(parameters.granularity); |
2788 | if (parameters.coarse != NoResult) |
2789 | texArgs.push_back(parameters.coarse); |
2790 | #endif |
2791 | |
2792 | // |
2793 | // Set up the optional arguments |
2794 | // |
2795 | size_t optArgNum = texArgs.size(); // the position of the mask for the optional arguments, if any. |
2796 | ImageOperandsMask mask = ImageOperandsMaskNone; // the mask operand |
2797 | if (parameters.bias) { |
2798 | mask = (ImageOperandsMask)(mask | ImageOperandsBiasMask); |
2799 | texArgs.push_back(parameters.bias); |
2800 | } |
2801 | if (parameters.lod) { |
2802 | mask = (ImageOperandsMask)(mask | ImageOperandsLodMask); |
2803 | texArgs.push_back(parameters.lod); |
2804 | explicitLod = true; |
2805 | } else if (parameters.gradX) { |
2806 | mask = (ImageOperandsMask)(mask | ImageOperandsGradMask); |
2807 | texArgs.push_back(parameters.gradX); |
2808 | texArgs.push_back(parameters.gradY); |
2809 | explicitLod = true; |
2810 | } else if (noImplicitLod && ! fetch && ! gather) { |
2811 | // have to explicitly use lod of 0 if not allowed to have them be implicit, and |
2812 | // we would otherwise be about to issue an implicit instruction |
2813 | mask = (ImageOperandsMask)(mask | ImageOperandsLodMask); |
2814 | texArgs.push_back(makeFloatConstant(0.0)); |
2815 | explicitLod = true; |
2816 | } |
2817 | if (parameters.offset) { |
2818 | if (isConstant(parameters.offset)) |
2819 | mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetMask); |
2820 | else { |
2821 | addCapability(CapabilityImageGatherExtended); |
2822 | mask = (ImageOperandsMask)(mask | ImageOperandsOffsetMask); |
2823 | } |
2824 | texArgs.push_back(parameters.offset); |
2825 | } |
2826 | if (parameters.offsets) { |
2827 | addCapability(CapabilityImageGatherExtended); |
2828 | mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetsMask); |
2829 | texArgs.push_back(parameters.offsets); |
2830 | } |
2831 | #ifndef GLSLANG_WEB |
2832 | if (parameters.sample) { |
2833 | mask = (ImageOperandsMask)(mask | ImageOperandsSampleMask); |
2834 | texArgs.push_back(parameters.sample); |
2835 | } |
2836 | if (parameters.lodClamp) { |
2837 | // capability if this bit is used |
2838 | addCapability(CapabilityMinLod); |
2839 | |
2840 | mask = (ImageOperandsMask)(mask | ImageOperandsMinLodMask); |
2841 | texArgs.push_back(parameters.lodClamp); |
2842 | } |
2843 | if (parameters.nonprivate) { |
2844 | mask = mask | ImageOperandsNonPrivateTexelKHRMask; |
2845 | } |
2846 | if (parameters.volatil) { |
2847 | mask = mask | ImageOperandsVolatileTexelKHRMask; |
2848 | } |
2849 | #endif |
2850 | mask = mask | signExtensionMask; |
2851 | // insert the operand for the mask, if any bits were set. |
2852 | if (mask != ImageOperandsMaskNone) |
2853 | texArgs.insert(texArgs.begin() + optArgNum, mask); |
2854 | |
2855 | // |
2856 | // Set up the instruction |
2857 | // |
2858 | Op opCode = OpNop; // All paths below need to set this |
2859 | if (fetch) { |
2860 | if (sparse) |
2861 | opCode = OpImageSparseFetch; |
2862 | else |
2863 | opCode = OpImageFetch; |
2864 | #ifndef GLSLANG_WEB |
2865 | } else if (parameters.granularity && parameters.coarse) { |
2866 | opCode = OpImageSampleFootprintNV; |
2867 | } else if (gather) { |
2868 | if (parameters.Dref) |
2869 | if (sparse) |
2870 | opCode = OpImageSparseDrefGather; |
2871 | else |
2872 | opCode = OpImageDrefGather; |
2873 | else |
2874 | if (sparse) |
2875 | opCode = OpImageSparseGather; |
2876 | else |
2877 | opCode = OpImageGather; |
2878 | #endif |
2879 | } else if (explicitLod) { |
2880 | if (parameters.Dref) { |
2881 | if (proj) |
2882 | if (sparse) |
2883 | opCode = OpImageSparseSampleProjDrefExplicitLod; |
2884 | else |
2885 | opCode = OpImageSampleProjDrefExplicitLod; |
2886 | else |
2887 | if (sparse) |
2888 | opCode = OpImageSparseSampleDrefExplicitLod; |
2889 | else |
2890 | opCode = OpImageSampleDrefExplicitLod; |
2891 | } else { |
2892 | if (proj) |
2893 | if (sparse) |
2894 | opCode = OpImageSparseSampleProjExplicitLod; |
2895 | else |
2896 | opCode = OpImageSampleProjExplicitLod; |
2897 | else |
2898 | if (sparse) |
2899 | opCode = OpImageSparseSampleExplicitLod; |
2900 | else |
2901 | opCode = OpImageSampleExplicitLod; |
2902 | } |
2903 | } else { |
2904 | if (parameters.Dref) { |
2905 | if (proj) |
2906 | if (sparse) |
2907 | opCode = OpImageSparseSampleProjDrefImplicitLod; |
2908 | else |
2909 | opCode = OpImageSampleProjDrefImplicitLod; |
2910 | else |
2911 | if (sparse) |
2912 | opCode = OpImageSparseSampleDrefImplicitLod; |
2913 | else |
2914 | opCode = OpImageSampleDrefImplicitLod; |
2915 | } else { |
2916 | if (proj) |
2917 | if (sparse) |
2918 | opCode = OpImageSparseSampleProjImplicitLod; |
2919 | else |
2920 | opCode = OpImageSampleProjImplicitLod; |
2921 | else |
2922 | if (sparse) |
2923 | opCode = OpImageSparseSampleImplicitLod; |
2924 | else |
2925 | opCode = OpImageSampleImplicitLod; |
2926 | } |
2927 | } |
2928 | |
2929 | // See if the result type is expecting a smeared result. |
2930 | // This happens when a legacy shadow*() call is made, which |
2931 | // gets a vec4 back instead of a float. |
2932 | Id smearedType = resultType; |
2933 | if (! isScalarType(resultType)) { |
2934 | switch (opCode) { |
2935 | case OpImageSampleDrefImplicitLod: |
2936 | case OpImageSampleDrefExplicitLod: |
2937 | case OpImageSampleProjDrefImplicitLod: |
2938 | case OpImageSampleProjDrefExplicitLod: |
2939 | resultType = getScalarTypeId(resultType); |
2940 | break; |
2941 | default: |
2942 | break; |
2943 | } |
2944 | } |
2945 | |
2946 | Id typeId0 = 0; |
2947 | Id typeId1 = 0; |
2948 | |
2949 | if (sparse) { |
2950 | typeId0 = resultType; |
2951 | typeId1 = getDerefTypeId(parameters.texelOut); |
2952 | resultType = makeStructResultType(typeId0, typeId1); |
2953 | } |
2954 | |
2955 | // Build the SPIR-V instruction |
2956 | Instruction* textureInst = new Instruction(getUniqueId(), resultType, opCode); |
2957 | for (size_t op = 0; op < optArgNum; ++op) |
2958 | textureInst->addIdOperand(texArgs[op]); |
2959 | if (optArgNum < texArgs.size()) |
2960 | textureInst->addImmediateOperand(texArgs[optArgNum]); |
2961 | for (size_t op = optArgNum + 1; op < texArgs.size(); ++op) |
2962 | textureInst->addIdOperand(texArgs[op]); |
2963 | setPrecision(textureInst->getResultId(), precision); |
2964 | buildPoint->addInstruction(std::unique_ptr<Instruction>(textureInst)); |
2965 | |
2966 | Id resultId = textureInst->getResultId(); |
2967 | |
2968 | if (sparse) { |
2969 | // set capability |
2970 | addCapability(CapabilitySparseResidency); |
2971 | |
2972 | // Decode the return type that was a special structure |
2973 | createStore(createCompositeExtract(resultId, typeId1, 1), parameters.texelOut); |
2974 | resultId = createCompositeExtract(resultId, typeId0, 0); |
2975 | setPrecision(resultId, precision); |
2976 | } else { |
2977 | // When a smear is needed, do it, as per what was computed |
2978 | // above when resultType was changed to a scalar type. |
2979 | if (resultType != smearedType) |
2980 | resultId = smearScalar(precision, resultId, smearedType); |
2981 | } |
2982 | |
2983 | return resultId; |
2984 | } |
2985 | |
2986 | // Comments in header |
2987 | Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameters, bool isUnsignedResult) |
2988 | { |
2989 | // Figure out the result type |
2990 | Id resultType = 0; |
2991 | switch (opCode) { |
2992 | case OpImageQuerySize: |
2993 | case OpImageQuerySizeLod: |
2994 | { |
2995 | int numComponents = 0; |
2996 | switch (getTypeDimensionality(getImageType(parameters.sampler))) { |
2997 | case Dim1D: |
2998 | case DimBuffer: |
2999 | numComponents = 1; |
3000 | break; |
3001 | case Dim2D: |
3002 | case DimCube: |
3003 | case DimRect: |
3004 | case DimSubpassData: |
3005 | numComponents = 2; |
3006 | break; |
3007 | case Dim3D: |
3008 | numComponents = 3; |
3009 | break; |
3010 | |
3011 | default: |
3012 | assert(0); |
3013 | break; |
3014 | } |
3015 | if (isArrayedImageType(getImageType(parameters.sampler))) |
3016 | ++numComponents; |
3017 | |
3018 | Id intType = isUnsignedResult ? makeUintType(32) : makeIntType(32); |
3019 | if (numComponents == 1) |
3020 | resultType = intType; |
3021 | else |
3022 | resultType = makeVectorType(intType, numComponents); |
3023 | |
3024 | break; |
3025 | } |
3026 | case OpImageQueryLod: |
3027 | resultType = makeVectorType(getScalarTypeId(getTypeId(parameters.coords)), 2); |
3028 | break; |
3029 | case OpImageQueryLevels: |
3030 | case OpImageQuerySamples: |
3031 | resultType = isUnsignedResult ? makeUintType(32) : makeIntType(32); |
3032 | break; |
3033 | default: |
3034 | assert(0); |
3035 | break; |
3036 | } |
3037 | |
3038 | Instruction* query = new Instruction(getUniqueId(), resultType, opCode); |
3039 | query->addIdOperand(parameters.sampler); |
3040 | if (parameters.coords) |
3041 | query->addIdOperand(parameters.coords); |
3042 | if (parameters.lod) |
3043 | query->addIdOperand(parameters.lod); |
3044 | buildPoint->addInstruction(std::unique_ptr<Instruction>(query)); |
3045 | addCapability(CapabilityImageQuery); |
3046 | |
3047 | return query->getResultId(); |
3048 | } |
3049 | |
3050 | // External comments in header. |
3051 | // Operates recursively to visit the composite's hierarchy. |
3052 | Id Builder::createCompositeCompare(Decoration precision, Id value1, Id value2, bool equal) |
3053 | { |
3054 | Id boolType = makeBoolType(); |
3055 | Id valueType = getTypeId(value1); |
3056 | |
3057 | Id resultId = NoResult; |
3058 | |
3059 | int numConstituents = getNumTypeConstituents(valueType); |
3060 | |
3061 | // Scalars and Vectors |
3062 | |
3063 | if (isScalarType(valueType) || isVectorType(valueType)) { |
3064 | assert(valueType == getTypeId(value2)); |
3065 | // These just need a single comparison, just have |
3066 | // to figure out what it is. |
3067 | Op op; |
3068 | switch (getMostBasicTypeClass(valueType)) { |
3069 | case OpTypeFloat: |
3070 | op = equal ? OpFOrdEqual : OpFUnordNotEqual; |
3071 | break; |
3072 | case OpTypeInt: |
3073 | default: |
3074 | op = equal ? OpIEqual : OpINotEqual; |
3075 | break; |
3076 | case OpTypeBool: |
3077 | op = equal ? OpLogicalEqual : OpLogicalNotEqual; |
3078 | precision = NoPrecision; |
3079 | break; |
3080 | } |
3081 | |
3082 | if (isScalarType(valueType)) { |
3083 | // scalar |
3084 | resultId = createBinOp(op, boolType, value1, value2); |
3085 | } else { |
3086 | // vector |
3087 | resultId = createBinOp(op, makeVectorType(boolType, numConstituents), value1, value2); |
3088 | setPrecision(resultId, precision); |
3089 | // reduce vector compares... |
3090 | resultId = createUnaryOp(equal ? OpAll : OpAny, boolType, resultId); |
3091 | } |
3092 | |
3093 | return setPrecision(resultId, precision); |
3094 | } |
3095 | |
3096 | // Only structs, arrays, and matrices should be left. |
3097 | // They share in common the reduction operation across their constituents. |
3098 | assert(isAggregateType(valueType) || isMatrixType(valueType)); |
3099 | |
3100 | // Compare each pair of constituents |
3101 | for (int constituent = 0; constituent < numConstituents; ++constituent) { |
3102 | std::vector<unsigned> indexes(1, constituent); |
3103 | Id constituentType1 = getContainedTypeId(getTypeId(value1), constituent); |
3104 | Id constituentType2 = getContainedTypeId(getTypeId(value2), constituent); |
3105 | Id constituent1 = createCompositeExtract(value1, constituentType1, indexes); |
3106 | Id constituent2 = createCompositeExtract(value2, constituentType2, indexes); |
3107 | |
3108 | Id subResultId = createCompositeCompare(precision, constituent1, constituent2, equal); |
3109 | |
3110 | if (constituent == 0) |
3111 | resultId = subResultId; |
3112 | else |
3113 | resultId = setPrecision(createBinOp(equal ? OpLogicalAnd : OpLogicalOr, boolType, resultId, subResultId), |
3114 | precision); |
3115 | } |
3116 | |
3117 | return resultId; |
3118 | } |
3119 | |
3120 | // OpCompositeConstruct |
3121 | Id Builder::createCompositeConstruct(Id typeId, const std::vector<Id>& constituents) |
3122 | { |
3123 | assert(isAggregateType(typeId) || (getNumTypeConstituents(typeId) > 1 && |
3124 | getNumTypeConstituents(typeId) == (int)constituents.size())); |
3125 | |
3126 | if (generatingOpCodeForSpecConst) { |
3127 | // Sometime, even in spec-constant-op mode, the constant composite to be |
3128 | // constructed may not be a specialization constant. |
3129 | // e.g.: |
3130 | // const mat2 m2 = mat2(a_spec_const, a_front_end_const, another_front_end_const, third_front_end_const); |
3131 | // The first column vector should be a spec constant one, as a_spec_const is a spec constant. |
3132 | // The second column vector should NOT be spec constant, as it does not contain any spec constants. |
3133 | // To handle such cases, we check the constituents of the constant vector to determine whether this |
3134 | // vector should be created as a spec constant. |
3135 | return makeCompositeConstant(typeId, constituents, |
3136 | std::any_of(constituents.begin(), constituents.end(), |
3137 | [&](spv::Id id) { return isSpecConstant(id); })); |
3138 | } |
3139 | |
3140 | Instruction* op = new Instruction(getUniqueId(), typeId, OpCompositeConstruct); |
3141 | for (int c = 0; c < (int)constituents.size(); ++c) |
3142 | op->addIdOperand(constituents[c]); |
3143 | buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); |
3144 | |
3145 | return op->getResultId(); |
3146 | } |
3147 | |
3148 | // Vector or scalar constructor |
3149 | Id Builder::createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId) |
3150 | { |
3151 | Id result = NoResult; |
3152 | unsigned int numTargetComponents = getNumTypeComponents(resultTypeId); |
3153 | unsigned int targetComponent = 0; |
3154 | |
3155 | // Special case: when calling a vector constructor with a single scalar |
3156 | // argument, smear the scalar |
3157 | if (sources.size() == 1 && isScalar(sources[0]) && numTargetComponents > 1) |
3158 | return smearScalar(precision, sources[0], resultTypeId); |
3159 | |
3160 | // accumulate the arguments for OpCompositeConstruct |
3161 | std::vector<Id> constituents; |
3162 | Id scalarTypeId = getScalarTypeId(resultTypeId); |
3163 | |
3164 | // lambda to store the result of visiting an argument component |
3165 | const auto latchResult = [&](Id comp) { |
3166 | if (numTargetComponents > 1) |
3167 | constituents.push_back(comp); |
3168 | else |
3169 | result = comp; |
3170 | ++targetComponent; |
3171 | }; |
3172 | |
3173 | // lambda to visit a vector argument's components |
3174 | const auto accumulateVectorConstituents = [&](Id sourceArg) { |
3175 | unsigned int sourceSize = getNumComponents(sourceArg); |
3176 | unsigned int sourcesToUse = sourceSize; |
3177 | if (sourcesToUse + targetComponent > numTargetComponents) |
3178 | sourcesToUse = numTargetComponents - targetComponent; |
3179 | |
3180 | for (unsigned int s = 0; s < sourcesToUse; ++s) { |
3181 | std::vector<unsigned> swiz; |
3182 | swiz.push_back(s); |
3183 | latchResult(createRvalueSwizzle(precision, scalarTypeId, sourceArg, swiz)); |
3184 | } |
3185 | }; |
3186 | |
3187 | // lambda to visit a matrix argument's components |
3188 | const auto accumulateMatrixConstituents = [&](Id sourceArg) { |
3189 | unsigned int sourceSize = getNumColumns(sourceArg) * getNumRows(sourceArg); |
3190 | unsigned int sourcesToUse = sourceSize; |
3191 | if (sourcesToUse + targetComponent > numTargetComponents) |
3192 | sourcesToUse = numTargetComponents - targetComponent; |
3193 | |
3194 | int col = 0; |
3195 | int row = 0; |
3196 | for (unsigned int s = 0; s < sourcesToUse; ++s) { |
3197 | if (row >= getNumRows(sourceArg)) { |
3198 | row = 0; |
3199 | col++; |
3200 | } |
3201 | std::vector<Id> indexes; |
3202 | indexes.push_back(col); |
3203 | indexes.push_back(row); |
3204 | latchResult(createCompositeExtract(sourceArg, scalarTypeId, indexes)); |
3205 | row++; |
3206 | } |
3207 | }; |
3208 | |
3209 | // Go through the source arguments, each one could have either |
3210 | // a single or multiple components to contribute. |
3211 | for (unsigned int i = 0; i < sources.size(); ++i) { |
3212 | |
3213 | if (isScalar(sources[i]) || isPointer(sources[i])) |
3214 | latchResult(sources[i]); |
3215 | else if (isVector(sources[i])) |
3216 | accumulateVectorConstituents(sources[i]); |
3217 | else if (isMatrix(sources[i])) |
3218 | accumulateMatrixConstituents(sources[i]); |
3219 | else |
3220 | assert(0); |
3221 | |
3222 | if (targetComponent >= numTargetComponents) |
3223 | break; |
3224 | } |
3225 | |
3226 | // If the result is a vector, make it from the gathered constituents. |
3227 | if (constituents.size() > 0) |
3228 | result = createCompositeConstruct(resultTypeId, constituents); |
3229 | |
3230 | return setPrecision(result, precision); |
3231 | } |
3232 | |
3233 | // Comments in header |
3234 | Id Builder::createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId) |
3235 | { |
3236 | Id componentTypeId = getScalarTypeId(resultTypeId); |
3237 | int numCols = getTypeNumColumns(resultTypeId); |
3238 | int numRows = getTypeNumRows(resultTypeId); |
3239 | |
3240 | Instruction* instr = module.getInstruction(componentTypeId); |
3241 | #ifdef GLSLANG_WEB |
3242 | const unsigned bitCount = 32; |
3243 | assert(bitCount == instr->getImmediateOperand(0)); |
3244 | #else |
3245 | const unsigned bitCount = instr->getImmediateOperand(0); |
3246 | #endif |
3247 | |
3248 | // Optimize matrix constructed from a bigger matrix |
3249 | if (isMatrix(sources[0]) && getNumColumns(sources[0]) >= numCols && getNumRows(sources[0]) >= numRows) { |
3250 | // To truncate the matrix to a smaller number of rows/columns, we need to: |
3251 | // 1. For each column, extract the column and truncate it to the required size using shuffle |
3252 | // 2. Assemble the resulting matrix from all columns |
3253 | Id matrix = sources[0]; |
3254 | Id columnTypeId = getContainedTypeId(resultTypeId); |
3255 | Id sourceColumnTypeId = getContainedTypeId(getTypeId(matrix)); |
3256 | |
3257 | std::vector<unsigned> channels; |
3258 | for (int row = 0; row < numRows; ++row) |
3259 | channels.push_back(row); |
3260 | |
3261 | std::vector<Id> matrixColumns; |
3262 | for (int col = 0; col < numCols; ++col) { |
3263 | std::vector<unsigned> indexes; |
3264 | indexes.push_back(col); |
3265 | Id colv = createCompositeExtract(matrix, sourceColumnTypeId, indexes); |
3266 | setPrecision(colv, precision); |
3267 | |
3268 | if (numRows != getNumRows(matrix)) { |
3269 | matrixColumns.push_back(createRvalueSwizzle(precision, columnTypeId, colv, channels)); |
3270 | } else { |
3271 | matrixColumns.push_back(colv); |
3272 | } |
3273 | } |
3274 | |
3275 | return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision); |
3276 | } |
3277 | |
3278 | // Otherwise, will use a two step process |
3279 | // 1. make a compile-time 2D array of values |
3280 | // 2. construct a matrix from that array |
3281 | |
3282 | // Step 1. |
3283 | |
3284 | // initialize the array to the identity matrix |
3285 | Id ids[maxMatrixSize][maxMatrixSize]; |
3286 | Id one = (bitCount == 64 ? makeDoubleConstant(1.0) : makeFloatConstant(1.0)); |
3287 | Id zero = (bitCount == 64 ? makeDoubleConstant(0.0) : makeFloatConstant(0.0)); |
3288 | for (int col = 0; col < 4; ++col) { |
3289 | for (int row = 0; row < 4; ++row) { |
3290 | if (col == row) |
3291 | ids[col][row] = one; |
3292 | else |
3293 | ids[col][row] = zero; |
3294 | } |
3295 | } |
3296 | |
3297 | // modify components as dictated by the arguments |
3298 | if (sources.size() == 1 && isScalar(sources[0])) { |
3299 | // a single scalar; resets the diagonals |
3300 | for (int col = 0; col < 4; ++col) |
3301 | ids[col][col] = sources[0]; |
3302 | } else if (isMatrix(sources[0])) { |
3303 | // constructing from another matrix; copy over the parts that exist in both the argument and constructee |
3304 | Id matrix = sources[0]; |
3305 | int minCols = std::min(numCols, getNumColumns(matrix)); |
3306 | int minRows = std::min(numRows, getNumRows(matrix)); |
3307 | for (int col = 0; col < minCols; ++col) { |
3308 | std::vector<unsigned> indexes; |
3309 | indexes.push_back(col); |
3310 | for (int row = 0; row < minRows; ++row) { |
3311 | indexes.push_back(row); |
3312 | ids[col][row] = createCompositeExtract(matrix, componentTypeId, indexes); |
3313 | indexes.pop_back(); |
3314 | setPrecision(ids[col][row], precision); |
3315 | } |
3316 | } |
3317 | } else { |
3318 | // fill in the matrix in column-major order with whatever argument components are available |
3319 | int row = 0; |
3320 | int col = 0; |
3321 | |
3322 | for (int arg = 0; arg < (int)sources.size() && col < numCols; ++arg) { |
3323 | Id argComp = sources[arg]; |
3324 | for (int comp = 0; comp < getNumComponents(sources[arg]); ++comp) { |
3325 | if (getNumComponents(sources[arg]) > 1) { |
3326 | argComp = createCompositeExtract(sources[arg], componentTypeId, comp); |
3327 | setPrecision(argComp, precision); |
3328 | } |
3329 | ids[col][row++] = argComp; |
3330 | if (row == numRows) { |
3331 | row = 0; |
3332 | col++; |
3333 | } |
3334 | if (col == numCols) { |
3335 | // If more components are provided than fit the matrix, discard the rest. |
3336 | break; |
3337 | } |
3338 | } |
3339 | } |
3340 | } |
3341 | |
3342 | // Step 2: Construct a matrix from that array. |
3343 | // First make the column vectors, then make the matrix. |
3344 | |
3345 | // make the column vectors |
3346 | Id columnTypeId = getContainedTypeId(resultTypeId); |
3347 | std::vector<Id> matrixColumns; |
3348 | for (int col = 0; col < numCols; ++col) { |
3349 | std::vector<Id> vectorComponents; |
3350 | for (int row = 0; row < numRows; ++row) |
3351 | vectorComponents.push_back(ids[col][row]); |
3352 | Id column = createCompositeConstruct(columnTypeId, vectorComponents); |
3353 | setPrecision(column, precision); |
3354 | matrixColumns.push_back(column); |
3355 | } |
3356 | |
3357 | // make the matrix |
3358 | return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision); |
3359 | } |
3360 | |
3361 | // Comments in header |
3362 | Builder::If::If(Id cond, unsigned int ctrl, Builder& gb) : |
3363 | builder(gb), |
3364 | condition(cond), |
3365 | control(ctrl), |
3366 | elseBlock(nullptr) |
3367 | { |
3368 | function = &builder.getBuildPoint()->getParent(); |
3369 | |
3370 | // make the blocks, but only put the then-block into the function, |
3371 | // the else-block and merge-block will be added later, in order, after |
3372 | // earlier code is emitted |
3373 | thenBlock = new Block(builder.getUniqueId(), *function); |
3374 | mergeBlock = new Block(builder.getUniqueId(), *function); |
3375 | |
3376 | // Save the current block, so that we can add in the flow control split when |
3377 | // makeEndIf is called. |
3378 | headerBlock = builder.getBuildPoint(); |
3379 | |
3380 | function->addBlock(thenBlock); |
3381 | builder.setBuildPoint(thenBlock); |
3382 | } |
3383 | |
3384 | // Comments in header |
3385 | void Builder::If::makeBeginElse() |
3386 | { |
3387 | // Close out the "then" by having it jump to the mergeBlock |
3388 | builder.createBranch(mergeBlock); |
3389 | |
3390 | // Make the first else block and add it to the function |
3391 | elseBlock = new Block(builder.getUniqueId(), *function); |
3392 | function->addBlock(elseBlock); |
3393 | |
3394 | // Start building the else block |
3395 | builder.setBuildPoint(elseBlock); |
3396 | } |
3397 | |
3398 | // Comments in header |
3399 | void Builder::If::makeEndIf() |
3400 | { |
3401 | // jump to the merge block |
3402 | builder.createBranch(mergeBlock); |
3403 | |
3404 | // Go back to the headerBlock and make the flow control split |
3405 | builder.setBuildPoint(headerBlock); |
3406 | builder.createSelectionMerge(mergeBlock, control); |
3407 | if (elseBlock) |
3408 | builder.createConditionalBranch(condition, thenBlock, elseBlock); |
3409 | else |
3410 | builder.createConditionalBranch(condition, thenBlock, mergeBlock); |
3411 | |
3412 | // add the merge block to the function |
3413 | function->addBlock(mergeBlock); |
3414 | builder.setBuildPoint(mergeBlock); |
3415 | } |
3416 | |
3417 | // Comments in header |
3418 | void Builder::makeSwitch(Id selector, unsigned int control, int numSegments, const std::vector<int>& caseValues, |
3419 | const std::vector<int>& valueIndexToSegment, int defaultSegment, |
3420 | std::vector<Block*>& segmentBlocks) |
3421 | { |
3422 | Function& function = buildPoint->getParent(); |
3423 | |
3424 | // make all the blocks |
3425 | for (int s = 0; s < numSegments; ++s) |
3426 | segmentBlocks.push_back(new Block(getUniqueId(), function)); |
3427 | |
3428 | Block* mergeBlock = new Block(getUniqueId(), function); |
3429 | |
3430 | // make and insert the switch's selection-merge instruction |
3431 | createSelectionMerge(mergeBlock, control); |
3432 | |
3433 | // make the switch instruction |
3434 | Instruction* switchInst = new Instruction(NoResult, NoType, OpSwitch); |
3435 | switchInst->addIdOperand(selector); |
3436 | auto defaultOrMerge = (defaultSegment >= 0) ? segmentBlocks[defaultSegment] : mergeBlock; |
3437 | switchInst->addIdOperand(defaultOrMerge->getId()); |
3438 | defaultOrMerge->addPredecessor(buildPoint); |
3439 | for (int i = 0; i < (int)caseValues.size(); ++i) { |
3440 | switchInst->addImmediateOperand(caseValues[i]); |
3441 | switchInst->addIdOperand(segmentBlocks[valueIndexToSegment[i]]->getId()); |
3442 | segmentBlocks[valueIndexToSegment[i]]->addPredecessor(buildPoint); |
3443 | } |
3444 | buildPoint->addInstruction(std::unique_ptr<Instruction>(switchInst)); |
3445 | |
3446 | // push the merge block |
3447 | switchMerges.push(mergeBlock); |
3448 | } |
3449 | |
3450 | // Comments in header |
3451 | void Builder::addSwitchBreak() |
3452 | { |
3453 | // branch to the top of the merge block stack |
3454 | createBranch(switchMerges.top()); |
3455 | createAndSetNoPredecessorBlock("post-switch-break" ); |
3456 | } |
3457 | |
3458 | // Comments in header |
3459 | void Builder::nextSwitchSegment(std::vector<Block*>& segmentBlock, int nextSegment) |
3460 | { |
3461 | int lastSegment = nextSegment - 1; |
3462 | if (lastSegment >= 0) { |
3463 | // Close out previous segment by jumping, if necessary, to next segment |
3464 | if (! buildPoint->isTerminated()) |
3465 | createBranch(segmentBlock[nextSegment]); |
3466 | } |
3467 | Block* block = segmentBlock[nextSegment]; |
3468 | block->getParent().addBlock(block); |
3469 | setBuildPoint(block); |
3470 | } |
3471 | |
3472 | // Comments in header |
3473 | void Builder::endSwitch(std::vector<Block*>& /*segmentBlock*/) |
3474 | { |
3475 | // Close out previous segment by jumping, if necessary, to next segment |
3476 | if (! buildPoint->isTerminated()) |
3477 | addSwitchBreak(); |
3478 | |
3479 | switchMerges.top()->getParent().addBlock(switchMerges.top()); |
3480 | setBuildPoint(switchMerges.top()); |
3481 | |
3482 | switchMerges.pop(); |
3483 | } |
3484 | |
3485 | Block& Builder::makeNewBlock() |
3486 | { |
3487 | Function& function = buildPoint->getParent(); |
3488 | auto block = new Block(getUniqueId(), function); |
3489 | function.addBlock(block); |
3490 | return *block; |
3491 | } |
3492 | |
3493 | Builder::LoopBlocks& Builder::makeNewLoop() |
3494 | { |
3495 | // This verbosity is needed to simultaneously get the same behavior |
3496 | // everywhere (id's in the same order), have a syntax that works |
3497 | // across lots of versions of C++, have no warnings from pedantic |
3498 | // compilation modes, and leave the rest of the code alone. |
3499 | Block& head = makeNewBlock(); |
3500 | Block& body = makeNewBlock(); |
3501 | Block& merge = makeNewBlock(); |
3502 | Block& continue_target = makeNewBlock(); |
3503 | LoopBlocks blocks(head, body, merge, continue_target); |
3504 | loops.push(blocks); |
3505 | return loops.top(); |
3506 | } |
3507 | |
3508 | void Builder::createLoopContinue() |
3509 | { |
3510 | createBranch(&loops.top().continue_target); |
3511 | // Set up a block for dead code. |
3512 | createAndSetNoPredecessorBlock("post-loop-continue" ); |
3513 | } |
3514 | |
3515 | void Builder::createLoopExit() |
3516 | { |
3517 | createBranch(&loops.top().merge); |
3518 | // Set up a block for dead code. |
3519 | createAndSetNoPredecessorBlock("post-loop-break" ); |
3520 | } |
3521 | |
3522 | void Builder::closeLoop() |
3523 | { |
3524 | loops.pop(); |
3525 | } |
3526 | |
3527 | void Builder::clearAccessChain() |
3528 | { |
3529 | accessChain.base = NoResult; |
3530 | accessChain.indexChain.clear(); |
3531 | accessChain.instr = NoResult; |
3532 | accessChain.swizzle.clear(); |
3533 | accessChain.component = NoResult; |
3534 | accessChain.preSwizzleBaseType = NoType; |
3535 | accessChain.isRValue = false; |
3536 | accessChain.coherentFlags.clear(); |
3537 | accessChain.alignment = 0; |
3538 | } |
3539 | |
3540 | // Comments in header |
3541 | void Builder::accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType, |
3542 | AccessChain::CoherentFlags coherentFlags, unsigned int alignment) |
3543 | { |
3544 | accessChain.coherentFlags |= coherentFlags; |
3545 | accessChain.alignment |= alignment; |
3546 | |
3547 | // swizzles can be stacked in GLSL, but simplified to a single |
3548 | // one here; the base type doesn't change |
3549 | if (accessChain.preSwizzleBaseType == NoType) |
3550 | accessChain.preSwizzleBaseType = preSwizzleBaseType; |
3551 | |
3552 | // if needed, propagate the swizzle for the current access chain |
3553 | if (accessChain.swizzle.size() > 0) { |
3554 | std::vector<unsigned> oldSwizzle = accessChain.swizzle; |
3555 | accessChain.swizzle.resize(0); |
3556 | for (unsigned int i = 0; i < swizzle.size(); ++i) { |
3557 | assert(swizzle[i] < oldSwizzle.size()); |
3558 | accessChain.swizzle.push_back(oldSwizzle[swizzle[i]]); |
3559 | } |
3560 | } else |
3561 | accessChain.swizzle = swizzle; |
3562 | |
3563 | // determine if we need to track this swizzle anymore |
3564 | simplifyAccessChainSwizzle(); |
3565 | } |
3566 | |
3567 | // Comments in header |
3568 | void Builder::accessChainStore(Id rvalue, Decoration nonUniform, spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment) |
3569 | { |
3570 | assert(accessChain.isRValue == false); |
3571 | |
3572 | transferAccessChainSwizzle(true); |
3573 | |
3574 | // If a swizzle exists and is not full and is not dynamic, then the swizzle will be broken into individual stores. |
3575 | if (accessChain.swizzle.size() > 0 && |
3576 | getNumTypeComponents(getResultingAccessChainType()) != (int)accessChain.swizzle.size() && |
3577 | accessChain.component == NoResult) { |
3578 | for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) { |
3579 | accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle[i])); |
3580 | accessChain.instr = NoResult; |
3581 | |
3582 | Id base = collapseAccessChain(); |
3583 | addDecoration(base, nonUniform); |
3584 | |
3585 | accessChain.indexChain.pop_back(); |
3586 | accessChain.instr = NoResult; |
3587 | |
3588 | // dynamic component should be gone |
3589 | assert(accessChain.component == NoResult); |
3590 | |
3591 | Id source = createCompositeExtract(rvalue, getContainedTypeId(getTypeId(rvalue)), i); |
3592 | |
3593 | // take LSB of alignment |
3594 | alignment = alignment & ~(alignment & (alignment-1)); |
3595 | if (getStorageClass(base) == StorageClassPhysicalStorageBufferEXT) { |
3596 | memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask); |
3597 | } |
3598 | |
3599 | createStore(source, base, memoryAccess, scope, alignment); |
3600 | } |
3601 | } |
3602 | else { |
3603 | Id base = collapseAccessChain(); |
3604 | addDecoration(base, nonUniform); |
3605 | |
3606 | Id source = rvalue; |
3607 | |
3608 | // dynamic component should be gone |
3609 | assert(accessChain.component == NoResult); |
3610 | |
3611 | // If swizzle still exists, it may be out-of-order, we must load the target vector, |
3612 | // extract and insert elements to perform writeMask and/or swizzle. |
3613 | if (accessChain.swizzle.size() > 0) { |
3614 | Id tempBaseId = createLoad(base, spv::NoPrecision); |
3615 | source = createLvalueSwizzle(getTypeId(tempBaseId), tempBaseId, source, accessChain.swizzle); |
3616 | } |
3617 | |
3618 | // take LSB of alignment |
3619 | alignment = alignment & ~(alignment & (alignment-1)); |
3620 | if (getStorageClass(base) == StorageClassPhysicalStorageBufferEXT) { |
3621 | memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask); |
3622 | } |
3623 | |
3624 | createStore(source, base, memoryAccess, scope, alignment); |
3625 | } |
3626 | } |
3627 | |
3628 | // Comments in header |
3629 | Id Builder::accessChainLoad(Decoration precision, Decoration l_nonUniform, |
3630 | Decoration r_nonUniform, Id resultType, spv::MemoryAccessMask memoryAccess, |
3631 | spv::Scope scope, unsigned int alignment) |
3632 | { |
3633 | Id id; |
3634 | |
3635 | if (accessChain.isRValue) { |
3636 | // transfer access chain, but try to stay in registers |
3637 | transferAccessChainSwizzle(false); |
3638 | if (accessChain.indexChain.size() > 0) { |
3639 | Id swizzleBase = accessChain.preSwizzleBaseType != NoType ? accessChain.preSwizzleBaseType : resultType; |
3640 | |
3641 | // if all the accesses are constants, we can use OpCompositeExtract |
3642 | std::vector<unsigned> indexes; |
3643 | bool constant = true; |
3644 | for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) { |
3645 | if (isConstantScalar(accessChain.indexChain[i])) |
3646 | indexes.push_back(getConstantScalar(accessChain.indexChain[i])); |
3647 | else { |
3648 | constant = false; |
3649 | break; |
3650 | } |
3651 | } |
3652 | |
3653 | if (constant) { |
3654 | id = createCompositeExtract(accessChain.base, swizzleBase, indexes); |
3655 | setPrecision(id, precision); |
3656 | } else { |
3657 | Id lValue = NoResult; |
3658 | if (spvVersion >= Spv_1_4 && isValidInitializer(accessChain.base)) { |
3659 | // make a new function variable for this r-value, using an initializer, |
3660 | // and mark it as NonWritable so that downstream it can be detected as a lookup |
3661 | // table |
3662 | lValue = createVariable(NoPrecision, StorageClassFunction, getTypeId(accessChain.base), |
3663 | "indexable" , accessChain.base); |
3664 | addDecoration(lValue, DecorationNonWritable); |
3665 | } else { |
3666 | lValue = createVariable(NoPrecision, StorageClassFunction, getTypeId(accessChain.base), |
3667 | "indexable" ); |
3668 | // store into it |
3669 | createStore(accessChain.base, lValue); |
3670 | } |
3671 | // move base to the new variable |
3672 | accessChain.base = lValue; |
3673 | accessChain.isRValue = false; |
3674 | |
3675 | // load through the access chain |
3676 | id = createLoad(collapseAccessChain(), precision); |
3677 | } |
3678 | } else |
3679 | id = accessChain.base; // no precision, it was set when this was defined |
3680 | } else { |
3681 | transferAccessChainSwizzle(true); |
3682 | |
3683 | // take LSB of alignment |
3684 | alignment = alignment & ~(alignment & (alignment-1)); |
3685 | if (getStorageClass(accessChain.base) == StorageClassPhysicalStorageBufferEXT) { |
3686 | memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask); |
3687 | } |
3688 | |
3689 | // load through the access chain |
3690 | id = collapseAccessChain(); |
3691 | // Apply nonuniform both to the access chain and the loaded value. |
3692 | // Buffer accesses need the access chain decorated, and this is where |
3693 | // loaded image types get decorated. TODO: This should maybe move to |
3694 | // createImageTextureFunctionCall. |
3695 | addDecoration(id, l_nonUniform); |
3696 | id = createLoad(id, precision, memoryAccess, scope, alignment); |
3697 | addDecoration(id, r_nonUniform); |
3698 | } |
3699 | |
3700 | // Done, unless there are swizzles to do |
3701 | if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult) |
3702 | return id; |
3703 | |
3704 | // Do remaining swizzling |
3705 | |
3706 | // Do the basic swizzle |
3707 | if (accessChain.swizzle.size() > 0) { |
3708 | Id swizzledType = getScalarTypeId(getTypeId(id)); |
3709 | if (accessChain.swizzle.size() > 1) |
3710 | swizzledType = makeVectorType(swizzledType, (int)accessChain.swizzle.size()); |
3711 | id = createRvalueSwizzle(precision, swizzledType, id, accessChain.swizzle); |
3712 | } |
3713 | |
3714 | // Do the dynamic component |
3715 | if (accessChain.component != NoResult) |
3716 | id = setPrecision(createVectorExtractDynamic(id, resultType, accessChain.component), precision); |
3717 | |
3718 | addDecoration(id, r_nonUniform); |
3719 | return id; |
3720 | } |
3721 | |
3722 | Id Builder::accessChainGetLValue() |
3723 | { |
3724 | assert(accessChain.isRValue == false); |
3725 | |
3726 | transferAccessChainSwizzle(true); |
3727 | Id lvalue = collapseAccessChain(); |
3728 | |
3729 | // If swizzle exists, it is out-of-order or not full, we must load the target vector, |
3730 | // extract and insert elements to perform writeMask and/or swizzle. This does not |
3731 | // go with getting a direct l-value pointer. |
3732 | assert(accessChain.swizzle.size() == 0); |
3733 | assert(accessChain.component == NoResult); |
3734 | |
3735 | return lvalue; |
3736 | } |
3737 | |
3738 | // comment in header |
3739 | Id Builder::accessChainGetInferredType() |
3740 | { |
3741 | // anything to operate on? |
3742 | if (accessChain.base == NoResult) |
3743 | return NoType; |
3744 | Id type = getTypeId(accessChain.base); |
3745 | |
3746 | // do initial dereference |
3747 | if (! accessChain.isRValue) |
3748 | type = getContainedTypeId(type); |
3749 | |
3750 | // dereference each index |
3751 | for (auto it = accessChain.indexChain.cbegin(); it != accessChain.indexChain.cend(); ++it) { |
3752 | if (isStructType(type)) |
3753 | type = getContainedTypeId(type, getConstantScalar(*it)); |
3754 | else |
3755 | type = getContainedTypeId(type); |
3756 | } |
3757 | |
3758 | // dereference swizzle |
3759 | if (accessChain.swizzle.size() == 1) |
3760 | type = getContainedTypeId(type); |
3761 | else if (accessChain.swizzle.size() > 1) |
3762 | type = makeVectorType(getContainedTypeId(type), (int)accessChain.swizzle.size()); |
3763 | |
3764 | // dereference component selection |
3765 | if (accessChain.component) |
3766 | type = getContainedTypeId(type); |
3767 | |
3768 | return type; |
3769 | } |
3770 | |
3771 | void Builder::dump(std::vector<unsigned int>& out) const |
3772 | { |
3773 | // Header, before first instructions: |
3774 | out.push_back(MagicNumber); |
3775 | out.push_back(spvVersion); |
3776 | out.push_back(builderNumber); |
3777 | out.push_back(uniqueId + 1); |
3778 | out.push_back(0); |
3779 | |
3780 | // Capabilities |
3781 | for (auto it = capabilities.cbegin(); it != capabilities.cend(); ++it) { |
3782 | Instruction capInst(0, 0, OpCapability); |
3783 | capInst.addImmediateOperand(*it); |
3784 | capInst.dump(out); |
3785 | } |
3786 | |
3787 | for (auto it = extensions.cbegin(); it != extensions.cend(); ++it) { |
3788 | Instruction extInst(0, 0, OpExtension); |
3789 | extInst.addStringOperand(it->c_str()); |
3790 | extInst.dump(out); |
3791 | } |
3792 | |
3793 | dumpInstructions(out, imports); |
3794 | Instruction memInst(0, 0, OpMemoryModel); |
3795 | memInst.addImmediateOperand(addressModel); |
3796 | memInst.addImmediateOperand(memoryModel); |
3797 | memInst.dump(out); |
3798 | |
3799 | // Instructions saved up while building: |
3800 | dumpInstructions(out, entryPoints); |
3801 | dumpInstructions(out, executionModes); |
3802 | |
3803 | // Debug instructions |
3804 | dumpInstructions(out, strings); |
3805 | dumpSourceInstructions(out); |
3806 | for (int e = 0; e < (int)sourceExtensions.size(); ++e) { |
3807 | Instruction sourceExtInst(0, 0, OpSourceExtension); |
3808 | sourceExtInst.addStringOperand(sourceExtensions[e]); |
3809 | sourceExtInst.dump(out); |
3810 | } |
3811 | dumpInstructions(out, names); |
3812 | dumpModuleProcesses(out); |
3813 | |
3814 | // Annotation instructions |
3815 | dumpInstructions(out, decorations); |
3816 | |
3817 | dumpInstructions(out, constantsTypesGlobals); |
3818 | dumpInstructions(out, externals); |
3819 | |
3820 | // The functions |
3821 | module.dump(out); |
3822 | } |
3823 | |
3824 | // |
3825 | // Protected methods. |
3826 | // |
3827 | |
3828 | // Turn the described access chain in 'accessChain' into an instruction(s) |
3829 | // computing its address. This *cannot* include complex swizzles, which must |
3830 | // be handled after this is called. |
3831 | // |
3832 | // Can generate code. |
3833 | Id Builder::collapseAccessChain() |
3834 | { |
3835 | assert(accessChain.isRValue == false); |
3836 | |
3837 | // did we already emit an access chain for this? |
3838 | if (accessChain.instr != NoResult) |
3839 | return accessChain.instr; |
3840 | |
3841 | // If we have a dynamic component, we can still transfer |
3842 | // that into a final operand to the access chain. We need to remap the |
3843 | // dynamic component through the swizzle to get a new dynamic component to |
3844 | // update. |
3845 | // |
3846 | // This was not done in transferAccessChainSwizzle() because it might |
3847 | // generate code. |
3848 | remapDynamicSwizzle(); |
3849 | if (accessChain.component != NoResult) { |
3850 | // transfer the dynamic component to the access chain |
3851 | accessChain.indexChain.push_back(accessChain.component); |
3852 | accessChain.component = NoResult; |
3853 | } |
3854 | |
3855 | // note that non-trivial swizzling is left pending |
3856 | |
3857 | // do we have an access chain? |
3858 | if (accessChain.indexChain.size() == 0) |
3859 | return accessChain.base; |
3860 | |
3861 | // emit the access chain |
3862 | StorageClass storageClass = (StorageClass)module.getStorageClass(getTypeId(accessChain.base)); |
3863 | accessChain.instr = createAccessChain(storageClass, accessChain.base, accessChain.indexChain); |
3864 | |
3865 | return accessChain.instr; |
3866 | } |
3867 | |
3868 | // For a dynamic component selection of a swizzle. |
3869 | // |
3870 | // Turn the swizzle and dynamic component into just a dynamic component. |
3871 | // |
3872 | // Generates code. |
3873 | void Builder::remapDynamicSwizzle() |
3874 | { |
3875 | // do we have a swizzle to remap a dynamic component through? |
3876 | if (accessChain.component != NoResult && accessChain.swizzle.size() > 1) { |
3877 | // build a vector of the swizzle for the component to map into |
3878 | std::vector<Id> components; |
3879 | for (int c = 0; c < (int)accessChain.swizzle.size(); ++c) |
3880 | components.push_back(makeUintConstant(accessChain.swizzle[c])); |
3881 | Id mapType = makeVectorType(makeUintType(32), (int)accessChain.swizzle.size()); |
3882 | Id map = makeCompositeConstant(mapType, components); |
3883 | |
3884 | // use it |
3885 | accessChain.component = createVectorExtractDynamic(map, makeUintType(32), accessChain.component); |
3886 | accessChain.swizzle.clear(); |
3887 | } |
3888 | } |
3889 | |
3890 | // clear out swizzle if it is redundant, that is reselecting the same components |
3891 | // that would be present without the swizzle. |
3892 | void Builder::simplifyAccessChainSwizzle() |
3893 | { |
3894 | // If the swizzle has fewer components than the vector, it is subsetting, and must stay |
3895 | // to preserve that fact. |
3896 | if (getNumTypeComponents(accessChain.preSwizzleBaseType) > (int)accessChain.swizzle.size()) |
3897 | return; |
3898 | |
3899 | // if components are out of order, it is a swizzle |
3900 | for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) { |
3901 | if (i != accessChain.swizzle[i]) |
3902 | return; |
3903 | } |
3904 | |
3905 | // otherwise, there is no need to track this swizzle |
3906 | accessChain.swizzle.clear(); |
3907 | if (accessChain.component == NoResult) |
3908 | accessChain.preSwizzleBaseType = NoType; |
3909 | } |
3910 | |
3911 | // To the extent any swizzling can become part of the chain |
3912 | // of accesses instead of a post operation, make it so. |
3913 | // If 'dynamic' is true, include transferring the dynamic component, |
3914 | // otherwise, leave it pending. |
3915 | // |
3916 | // Does not generate code. just updates the access chain. |
3917 | void Builder::transferAccessChainSwizzle(bool dynamic) |
3918 | { |
3919 | // non existent? |
3920 | if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult) |
3921 | return; |
3922 | |
3923 | // too complex? |
3924 | // (this requires either a swizzle, or generating code for a dynamic component) |
3925 | if (accessChain.swizzle.size() > 1) |
3926 | return; |
3927 | |
3928 | // single component, either in the swizzle and/or dynamic component |
3929 | if (accessChain.swizzle.size() == 1) { |
3930 | assert(accessChain.component == NoResult); |
3931 | // handle static component selection |
3932 | accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle.front())); |
3933 | accessChain.swizzle.clear(); |
3934 | accessChain.preSwizzleBaseType = NoType; |
3935 | } else if (dynamic && accessChain.component != NoResult) { |
3936 | assert(accessChain.swizzle.size() == 0); |
3937 | // handle dynamic component |
3938 | accessChain.indexChain.push_back(accessChain.component); |
3939 | accessChain.preSwizzleBaseType = NoType; |
3940 | accessChain.component = NoResult; |
3941 | } |
3942 | } |
3943 | |
3944 | // Utility method for creating a new block and setting the insert point to |
3945 | // be in it. This is useful for flow-control operations that need a "dummy" |
3946 | // block proceeding them (e.g. instructions after a discard, etc). |
3947 | void Builder::createAndSetNoPredecessorBlock(const char* /*name*/) |
3948 | { |
3949 | Block* block = new Block(getUniqueId(), buildPoint->getParent()); |
3950 | block->setUnreachable(); |
3951 | buildPoint->getParent().addBlock(block); |
3952 | setBuildPoint(block); |
3953 | |
3954 | // if (name) |
3955 | // addName(block->getId(), name); |
3956 | } |
3957 | |
3958 | // Comments in header |
3959 | void Builder::createBranch(Block* block) |
3960 | { |
3961 | Instruction* branch = new Instruction(OpBranch); |
3962 | branch->addIdOperand(block->getId()); |
3963 | buildPoint->addInstruction(std::unique_ptr<Instruction>(branch)); |
3964 | block->addPredecessor(buildPoint); |
3965 | } |
3966 | |
3967 | void Builder::createSelectionMerge(Block* mergeBlock, unsigned int control) |
3968 | { |
3969 | Instruction* merge = new Instruction(OpSelectionMerge); |
3970 | merge->addIdOperand(mergeBlock->getId()); |
3971 | merge->addImmediateOperand(control); |
3972 | buildPoint->addInstruction(std::unique_ptr<Instruction>(merge)); |
3973 | } |
3974 | |
3975 | void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, |
3976 | const std::vector<unsigned int>& operands) |
3977 | { |
3978 | Instruction* merge = new Instruction(OpLoopMerge); |
3979 | merge->addIdOperand(mergeBlock->getId()); |
3980 | merge->addIdOperand(continueBlock->getId()); |
3981 | merge->addImmediateOperand(control); |
3982 | for (int op = 0; op < (int)operands.size(); ++op) |
3983 | merge->addImmediateOperand(operands[op]); |
3984 | buildPoint->addInstruction(std::unique_ptr<Instruction>(merge)); |
3985 | } |
3986 | |
3987 | void Builder::createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock) |
3988 | { |
3989 | Instruction* branch = new Instruction(OpBranchConditional); |
3990 | branch->addIdOperand(condition); |
3991 | branch->addIdOperand(thenBlock->getId()); |
3992 | branch->addIdOperand(elseBlock->getId()); |
3993 | buildPoint->addInstruction(std::unique_ptr<Instruction>(branch)); |
3994 | thenBlock->addPredecessor(buildPoint); |
3995 | elseBlock->addPredecessor(buildPoint); |
3996 | } |
3997 | |
3998 | // OpSource |
3999 | // [OpSourceContinued] |
4000 | // ... |
4001 | void Builder::dumpSourceInstructions(const spv::Id fileId, const std::string& text, |
4002 | std::vector<unsigned int>& out) const |
4003 | { |
4004 | const int maxWordCount = 0xFFFF; |
4005 | const int opSourceWordCount = 4; |
4006 | const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount) - 1; |
4007 | |
4008 | if (sourceLang != SourceLanguageUnknown) { |
4009 | // OpSource Language Version File Source |
4010 | Instruction sourceInst(NoResult, NoType, OpSource); |
4011 | sourceInst.addImmediateOperand(sourceLang); |
4012 | sourceInst.addImmediateOperand(sourceVersion); |
4013 | // File operand |
4014 | if (fileId != NoResult) { |
4015 | sourceInst.addIdOperand(fileId); |
4016 | // Source operand |
4017 | if (text.size() > 0) { |
4018 | int nextByte = 0; |
4019 | std::string subString; |
4020 | while ((int)text.size() - nextByte > 0) { |
4021 | subString = text.substr(nextByte, nonNullBytesPerInstruction); |
4022 | if (nextByte == 0) { |
4023 | // OpSource |
4024 | sourceInst.addStringOperand(subString.c_str()); |
4025 | sourceInst.dump(out); |
4026 | } else { |
4027 | // OpSourcContinued |
4028 | Instruction sourceContinuedInst(OpSourceContinued); |
4029 | sourceContinuedInst.addStringOperand(subString.c_str()); |
4030 | sourceContinuedInst.dump(out); |
4031 | } |
4032 | nextByte += nonNullBytesPerInstruction; |
4033 | } |
4034 | } else |
4035 | sourceInst.dump(out); |
4036 | } else |
4037 | sourceInst.dump(out); |
4038 | } |
4039 | } |
4040 | |
4041 | // Dump an OpSource[Continued] sequence for the source and every include file |
4042 | void Builder::dumpSourceInstructions(std::vector<unsigned int>& out) const |
4043 | { |
4044 | if (emitNonSemanticShaderDebugInfo) return; |
4045 | dumpSourceInstructions(sourceFileStringId, sourceText, out); |
4046 | for (auto iItr = includeFiles.begin(); iItr != includeFiles.end(); ++iItr) |
4047 | dumpSourceInstructions(iItr->first, *iItr->second, out); |
4048 | } |
4049 | |
4050 | void Builder::dumpInstructions(std::vector<unsigned int>& out, |
4051 | const std::vector<std::unique_ptr<Instruction> >& instructions) const |
4052 | { |
4053 | for (int i = 0; i < (int)instructions.size(); ++i) { |
4054 | instructions[i]->dump(out); |
4055 | } |
4056 | } |
4057 | |
4058 | void Builder::dumpModuleProcesses(std::vector<unsigned int>& out) const |
4059 | { |
4060 | for (int i = 0; i < (int)moduleProcesses.size(); ++i) { |
4061 | Instruction moduleProcessed(OpModuleProcessed); |
4062 | moduleProcessed.addStringOperand(moduleProcesses[i]); |
4063 | moduleProcessed.dump(out); |
4064 | } |
4065 | } |
4066 | |
4067 | }; // end spv namespace |
4068 | |