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