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