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