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