/src/spirv-cross/spirv_cross_parsed_ir.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2018-2021 Arm Limited |
3 | | * SPDX-License-Identifier: Apache-2.0 OR MIT |
4 | | * |
5 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
6 | | * you may not use this file except in compliance with the License. |
7 | | * You may obtain a copy of the License at |
8 | | * |
9 | | * http://www.apache.org/licenses/LICENSE-2.0 |
10 | | * |
11 | | * Unless required by applicable law or agreed to in writing, software |
12 | | * distributed under the License is distributed on an "AS IS" BASIS, |
13 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 | | * See the License for the specific language governing permissions and |
15 | | * limitations under the License. |
16 | | */ |
17 | | |
18 | | /* |
19 | | * At your option, you may choose to accept this material under either: |
20 | | * 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or |
21 | | * 2. The MIT License, found at <http://opensource.org/licenses/MIT>. |
22 | | */ |
23 | | |
24 | | #include "spirv_cross_parsed_ir.hpp" |
25 | | #include <algorithm> |
26 | | #include <assert.h> |
27 | | |
28 | | using namespace std; |
29 | | using namespace SPIRV_CROSS_SPV_HEADER_NAMESPACE; |
30 | | |
31 | | namespace SPIRV_CROSS_NAMESPACE |
32 | | { |
33 | | ParsedIR::ParsedIR() |
34 | 255 | { |
35 | | // If we move ParsedIR, we need to make sure the pointer stays fixed since the child Variant objects consume a pointer to this group, |
36 | | // so need an extra pointer here. |
37 | 255 | pool_group.reset(new ObjectPoolGroup); |
38 | | |
39 | 255 | pool_group->pools[TypeType].reset(new ObjectPool<SPIRType>); |
40 | 255 | pool_group->pools[TypeVariable].reset(new ObjectPool<SPIRVariable>); |
41 | 255 | pool_group->pools[TypeConstant].reset(new ObjectPool<SPIRConstant>); |
42 | 255 | pool_group->pools[TypeFunction].reset(new ObjectPool<SPIRFunction>); |
43 | 255 | pool_group->pools[TypeFunctionPrototype].reset(new ObjectPool<SPIRFunctionPrototype>); |
44 | 255 | pool_group->pools[TypeBlock].reset(new ObjectPool<SPIRBlock>); |
45 | 255 | pool_group->pools[TypeExtension].reset(new ObjectPool<SPIRExtension>); |
46 | 255 | pool_group->pools[TypeExpression].reset(new ObjectPool<SPIRExpression>); |
47 | 255 | pool_group->pools[TypeConstantOp].reset(new ObjectPool<SPIRConstantOp>); |
48 | 255 | pool_group->pools[TypeCombinedImageSampler].reset(new ObjectPool<SPIRCombinedImageSampler>); |
49 | 255 | pool_group->pools[TypeAccessChain].reset(new ObjectPool<SPIRAccessChain>); |
50 | 255 | pool_group->pools[TypeUndef].reset(new ObjectPool<SPIRUndef>); |
51 | 255 | pool_group->pools[TypeString].reset(new ObjectPool<SPIRString>); |
52 | 255 | } |
53 | | |
54 | | // Should have been default-implemented, but need this on MSVC 2013. |
55 | | ParsedIR::ParsedIR(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT |
56 | 0 | { |
57 | 0 | *this = std::move(other); |
58 | 0 | } |
59 | | |
60 | | ParsedIR &ParsedIR::operator=(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT |
61 | 0 | { |
62 | 0 | if (this != &other) |
63 | 0 | { |
64 | 0 | pool_group = std::move(other.pool_group); |
65 | 0 | spirv = std::move(other.spirv); |
66 | 0 | meta = std::move(other.meta); |
67 | 0 | for (int i = 0; i < TypeCount; i++) |
68 | 0 | ids_for_type[i] = std::move(other.ids_for_type[i]); |
69 | 0 | ids_for_constant_undef_or_type = std::move(other.ids_for_constant_undef_or_type); |
70 | 0 | ids_for_constant_or_variable = std::move(other.ids_for_constant_or_variable); |
71 | 0 | declared_capabilities = std::move(other.declared_capabilities); |
72 | 0 | declared_extensions = std::move(other.declared_extensions); |
73 | 0 | block_meta = std::move(other.block_meta); |
74 | 0 | continue_block_to_loop_header = std::move(other.continue_block_to_loop_header); |
75 | 0 | entry_points = std::move(other.entry_points); |
76 | 0 | ids = std::move(other.ids); |
77 | 0 | addressing_model = other.addressing_model; |
78 | 0 | memory_model = other.memory_model; |
79 | |
|
80 | 0 | default_entry_point = other.default_entry_point; |
81 | 0 | source = other.source; |
82 | 0 | loop_iteration_depth_hard = other.loop_iteration_depth_hard; |
83 | 0 | loop_iteration_depth_soft = other.loop_iteration_depth_soft; |
84 | |
|
85 | 0 | meta_needing_name_fixup = std::move(other.meta_needing_name_fixup); |
86 | 0 | load_type_width = std::move(other.load_type_width); |
87 | 0 | } |
88 | 0 | return *this; |
89 | 0 | } |
90 | | |
91 | | ParsedIR::ParsedIR(const ParsedIR &other) |
92 | 0 | : ParsedIR() |
93 | 0 | { |
94 | 0 | *this = other; |
95 | 0 | } |
96 | | |
97 | | ParsedIR &ParsedIR::operator=(const ParsedIR &other) |
98 | 0 | { |
99 | 0 | if (this != &other) |
100 | 0 | { |
101 | 0 | spirv = other.spirv; |
102 | 0 | meta = other.meta; |
103 | 0 | for (int i = 0; i < TypeCount; i++) |
104 | 0 | ids_for_type[i] = other.ids_for_type[i]; |
105 | 0 | ids_for_constant_undef_or_type = other.ids_for_constant_undef_or_type; |
106 | 0 | ids_for_constant_or_variable = other.ids_for_constant_or_variable; |
107 | 0 | declared_capabilities = other.declared_capabilities; |
108 | 0 | declared_extensions = other.declared_extensions; |
109 | 0 | block_meta = other.block_meta; |
110 | 0 | continue_block_to_loop_header = other.continue_block_to_loop_header; |
111 | 0 | entry_points = other.entry_points; |
112 | 0 | default_entry_point = other.default_entry_point; |
113 | 0 | source = other.source; |
114 | 0 | loop_iteration_depth_hard = other.loop_iteration_depth_hard; |
115 | 0 | loop_iteration_depth_soft = other.loop_iteration_depth_soft; |
116 | 0 | addressing_model = other.addressing_model; |
117 | 0 | memory_model = other.memory_model; |
118 | | |
119 | |
|
120 | 0 | meta_needing_name_fixup = other.meta_needing_name_fixup; |
121 | 0 | load_type_width = other.load_type_width; |
122 | | |
123 | | // Very deliberate copying of IDs. There is no default copy constructor, nor a simple default constructor. |
124 | | // Construct object first so we have the correct allocator set-up, then we can copy object into our new pool group. |
125 | 0 | ids.clear(); |
126 | 0 | ids.reserve(other.ids.size()); |
127 | 0 | for (size_t i = 0; i < other.ids.size(); i++) |
128 | 0 | { |
129 | 0 | ids.emplace_back(pool_group.get()); |
130 | 0 | ids.back() = other.ids[i]; |
131 | 0 | } |
132 | 0 | } |
133 | 0 | return *this; |
134 | 0 | } |
135 | | |
136 | | void ParsedIR::set_id_bounds(uint32_t bounds) |
137 | 248 | { |
138 | 248 | ids.reserve(bounds); |
139 | 31.8M | while (ids.size() < bounds) |
140 | 31.8M | ids.emplace_back(pool_group.get()); |
141 | | |
142 | 248 | block_meta.resize(bounds); |
143 | 248 | } |
144 | | |
145 | | // Roll our own versions of these functions to avoid potential locale shenanigans. |
146 | | static bool is_alpha(char c) |
147 | 99.8k | { |
148 | 99.8k | return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); |
149 | 99.8k | } |
150 | | |
151 | | static bool is_numeric(char c) |
152 | 43.3k | { |
153 | 43.3k | return c >= '0' && c <= '9'; |
154 | 43.3k | } |
155 | | |
156 | | static bool is_alphanumeric(char c) |
157 | 99.8k | { |
158 | 99.8k | return is_alpha(c) || is_numeric(c); |
159 | 99.8k | } |
160 | | |
161 | | static bool is_valid_identifier(const string &name) |
162 | 13.7k | { |
163 | 13.7k | if (name.empty()) |
164 | 147 | return true; |
165 | | |
166 | 13.6k | if (is_numeric(name[0])) |
167 | 4 | return false; |
168 | | |
169 | 13.6k | for (auto c : name) |
170 | 99.8k | if (!is_alphanumeric(c) && c != '_') |
171 | 4.57k | return false; |
172 | | |
173 | 9.05k | bool saw_underscore = false; |
174 | | // Two underscores in a row is not a valid identifier either. |
175 | | // Technically reserved, but it's easier to treat it as invalid. |
176 | 9.05k | for (auto c : name) |
177 | 89.1k | { |
178 | 89.1k | bool is_underscore = c == '_'; |
179 | 89.1k | if (is_underscore && saw_underscore) |
180 | 1.26k | return false; |
181 | 87.8k | saw_underscore = is_underscore; |
182 | 87.8k | } |
183 | | |
184 | 7.78k | return true; |
185 | 9.05k | } |
186 | | |
187 | | static bool is_reserved_prefix(const string &name) |
188 | 7.93k | { |
189 | | // Generic reserved identifiers used by the implementation. |
190 | 7.93k | return name.compare(0, 3, "gl_", 3) == 0 || |
191 | | // Ignore this case for now, might rewrite internal code to always use spv prefix. |
192 | | //name.compare(0, 11, "SPIRV_Cross", 11) == 0 || |
193 | 7.85k | name.compare(0, 3, "spv", 3) == 0; |
194 | 7.93k | } |
195 | | |
196 | | static bool is_reserved_identifier(const string &name, bool member, bool allow_reserved_prefixes) |
197 | 7.93k | { |
198 | 7.93k | if (!allow_reserved_prefixes && is_reserved_prefix(name)) |
199 | 77 | return true; |
200 | | |
201 | 7.85k | if (member) |
202 | 1.27k | { |
203 | | // Reserved member identifiers come in one form: |
204 | | // _m[0-9]+$. |
205 | 1.27k | if (name.size() < 3) |
206 | 519 | return false; |
207 | | |
208 | 752 | if (name.compare(0, 2, "_m", 2) != 0) |
209 | 752 | return false; |
210 | | |
211 | 0 | size_t index = 2; |
212 | 0 | while (index < name.size() && is_numeric(name[index])) |
213 | 0 | index++; |
214 | |
|
215 | 0 | return index == name.size(); |
216 | 752 | } |
217 | 6.58k | else |
218 | 6.58k | { |
219 | | // Reserved non-member identifiers come in two forms: |
220 | | // _[0-9]+$, used for temporaries which map directly to a SPIR-V ID. |
221 | | // _[0-9]+_, used for auxillary temporaries which derived from a SPIR-V ID. |
222 | 6.58k | if (name.size() < 2) |
223 | 668 | return false; |
224 | | |
225 | 5.91k | if (name[0] != '_' || !is_numeric(name[1])) |
226 | 3.74k | return false; |
227 | | |
228 | 2.17k | size_t index = 2; |
229 | 6.77k | while (index < name.size() && is_numeric(name[index])) |
230 | 4.59k | index++; |
231 | | |
232 | 2.17k | return index == name.size() || (index < name.size() && name[index] == '_'); |
233 | 5.91k | } |
234 | 7.85k | } |
235 | | |
236 | | bool ParsedIR::is_globally_reserved_identifier(std::string &str, bool allow_reserved_prefixes) |
237 | 0 | { |
238 | 0 | return is_reserved_identifier(str, false, allow_reserved_prefixes); |
239 | 0 | } |
240 | | |
241 | | uint32_t ParsedIR::get_spirv_version() const |
242 | 0 | { |
243 | 0 | return spirv[1]; |
244 | 0 | } |
245 | | |
246 | | static string make_unreserved_identifier(const string &name) |
247 | 0 | { |
248 | 0 | if (is_reserved_prefix(name)) |
249 | 0 | return "_RESERVED_IDENTIFIER_FIXUP_" + name; |
250 | 0 | else |
251 | 0 | return "_RESERVED_IDENTIFIER_FIXUP" + name; |
252 | 0 | } |
253 | | |
254 | | void ParsedIR::sanitize_underscores(std::string &str) |
255 | 0 | { |
256 | | // Compact adjacent underscores to make it valid. |
257 | 0 | auto dst = str.begin(); |
258 | 0 | auto src = dst; |
259 | 0 | bool saw_underscore = false; |
260 | 0 | while (src != str.end()) |
261 | 0 | { |
262 | 0 | bool is_underscore = *src == '_'; |
263 | 0 | if (saw_underscore && is_underscore) |
264 | 0 | { |
265 | 0 | src++; |
266 | 0 | } |
267 | 0 | else |
268 | 0 | { |
269 | 0 | if (dst != src) |
270 | 0 | *dst = *src; |
271 | 0 | dst++; |
272 | 0 | src++; |
273 | 0 | saw_underscore = is_underscore; |
274 | 0 | } |
275 | 0 | } |
276 | 0 | str.erase(dst, str.end()); |
277 | 0 | } |
278 | | |
279 | | static string ensure_valid_identifier(const string &name) |
280 | 0 | { |
281 | | // Functions in glslangValidator are mangled with name(<mangled> stuff. |
282 | | // Normally, we would never see '(' in any legal identifiers, so just strip them out. |
283 | 0 | auto str = name.substr(0, name.find('(')); |
284 | |
|
285 | 0 | if (str.empty()) |
286 | 0 | return str; |
287 | | |
288 | 0 | if (is_numeric(str[0])) |
289 | 0 | str[0] = '_'; |
290 | |
|
291 | 0 | for (auto &c : str) |
292 | 0 | if (!is_alphanumeric(c) && c != '_') |
293 | 0 | c = '_'; |
294 | |
|
295 | 0 | ParsedIR::sanitize_underscores(str); |
296 | 0 | return str; |
297 | 0 | } |
298 | | |
299 | | const string &ParsedIR::get_name(ID id) const |
300 | 5.65k | { |
301 | 5.65k | auto *m = find_meta(id); |
302 | 5.65k | if (m) |
303 | 3.63k | return m->decoration.alias; |
304 | 2.01k | else |
305 | 2.01k | return empty_string; |
306 | 5.65k | } |
307 | | |
308 | | const string &ParsedIR::get_member_name(TypeID id, uint32_t index) const |
309 | 0 | { |
310 | 0 | auto *m = find_meta(id); |
311 | 0 | if (m) |
312 | 0 | { |
313 | 0 | if (index >= m->members.size()) |
314 | 0 | return empty_string; |
315 | 0 | return m->members[index].alias; |
316 | 0 | } |
317 | 0 | else |
318 | 0 | return empty_string; |
319 | 0 | } |
320 | | |
321 | | void ParsedIR::sanitize_identifier(std::string &name, bool member, bool allow_reserved_prefixes) |
322 | 0 | { |
323 | 0 | if (!is_valid_identifier(name)) |
324 | 0 | name = ensure_valid_identifier(name); |
325 | 0 | if (is_reserved_identifier(name, member, allow_reserved_prefixes)) |
326 | 0 | name = make_unreserved_identifier(name); |
327 | 0 | } |
328 | | |
329 | | void ParsedIR::fixup_reserved_names() |
330 | 0 | { |
331 | 0 | for (uint32_t id : meta_needing_name_fixup) |
332 | 0 | { |
333 | | // Don't rename remapped variables like 'gl_LastFragDepthARM'. |
334 | 0 | if (ids[id].get_type() == TypeVariable && get<SPIRVariable>(id).remapped_variable) |
335 | 0 | continue; |
336 | | |
337 | 0 | auto &m = meta[id]; |
338 | 0 | sanitize_identifier(m.decoration.alias, false, false); |
339 | 0 | for (auto &memb : m.members) |
340 | 0 | sanitize_identifier(memb.alias, true, false); |
341 | 0 | } |
342 | 0 | meta_needing_name_fixup.clear(); |
343 | 0 | } |
344 | | |
345 | | void ParsedIR::set_name(ID id, const string &name) |
346 | 10.3k | { |
347 | 10.3k | auto &m = meta[id]; |
348 | 10.3k | m.decoration.alias = name; |
349 | 10.3k | if (!is_valid_identifier(name) || is_reserved_identifier(name, false, false)) |
350 | 3.79k | meta_needing_name_fixup.insert(id); |
351 | 10.3k | } |
352 | | |
353 | | void ParsedIR::set_member_name(TypeID id, uint32_t index, const string &name) |
354 | 3.39k | { |
355 | 3.39k | auto &m = meta[id]; |
356 | 3.39k | m.members.resize(max(m.members.size(), size_t(index) + 1)); |
357 | 3.39k | m.members[index].alias = name; |
358 | 3.39k | if (!is_valid_identifier(name) || is_reserved_identifier(name, true, false)) |
359 | 2.12k | meta_needing_name_fixup.insert(id); |
360 | 3.39k | } |
361 | | |
362 | | void ParsedIR::set_decoration_string(ID id, Decoration decoration, const string &argument) |
363 | 0 | { |
364 | 0 | auto &dec = meta[id].decoration; |
365 | 0 | dec.decoration_flags.set(decoration); |
366 | |
|
367 | 0 | switch (decoration) |
368 | 0 | { |
369 | 0 | case DecorationHlslSemanticGOOGLE: |
370 | 0 | dec.hlsl_semantic = argument; |
371 | 0 | break; |
372 | | |
373 | 0 | case DecorationUserTypeGOOGLE: |
374 | 0 | dec.user_type = argument; |
375 | 0 | break; |
376 | | |
377 | 0 | default: |
378 | 0 | break; |
379 | 0 | } |
380 | 0 | } |
381 | | |
382 | | void ParsedIR::set_decoration(ID id, Decoration decoration, uint32_t argument) |
383 | 21.1k | { |
384 | 21.1k | auto &dec = meta[id].decoration; |
385 | 21.1k | dec.decoration_flags.set(decoration); |
386 | | |
387 | 21.1k | switch (decoration) |
388 | 21.1k | { |
389 | 1.34k | case DecorationBuiltIn: |
390 | 1.34k | dec.builtin = true; |
391 | 1.34k | dec.builtin_type = static_cast<BuiltIn>(argument); |
392 | 1.34k | break; |
393 | | |
394 | 1.12k | case DecorationLocation: |
395 | 1.12k | dec.location = argument; |
396 | 1.12k | break; |
397 | | |
398 | 5.38k | case DecorationComponent: |
399 | 5.38k | dec.component = argument; |
400 | 5.38k | break; |
401 | | |
402 | 138 | case DecorationOffset: |
403 | 138 | dec.offset = argument; |
404 | 138 | break; |
405 | | |
406 | 0 | case DecorationXfbBuffer: |
407 | 0 | dec.xfb_buffer = argument; |
408 | 0 | break; |
409 | | |
410 | 0 | case DecorationXfbStride: |
411 | 0 | dec.xfb_stride = argument; |
412 | 0 | break; |
413 | | |
414 | 0 | case DecorationStream: |
415 | 0 | dec.stream = argument; |
416 | 0 | break; |
417 | | |
418 | 7 | case DecorationArrayStride: |
419 | 7 | dec.array_stride = argument; |
420 | 7 | break; |
421 | | |
422 | 0 | case DecorationMatrixStride: |
423 | 0 | dec.matrix_stride = argument; |
424 | 0 | break; |
425 | | |
426 | 3.03k | case DecorationBinding: |
427 | 3.03k | dec.binding = argument; |
428 | 3.03k | break; |
429 | | |
430 | 1.73k | case DecorationDescriptorSet: |
431 | 1.73k | dec.set = argument; |
432 | 1.73k | break; |
433 | | |
434 | 0 | case DecorationInputAttachmentIndex: |
435 | 0 | dec.input_attachment = argument; |
436 | 0 | break; |
437 | | |
438 | 28 | case DecorationSpecId: |
439 | 28 | dec.spec_id = argument; |
440 | 28 | break; |
441 | | |
442 | 10 | case DecorationIndex: |
443 | 10 | dec.index = argument; |
444 | 10 | break; |
445 | | |
446 | 0 | case DecorationHlslCounterBufferGOOGLE: |
447 | 0 | meta[id].hlsl_magic_counter_buffer = argument; |
448 | 0 | meta[argument].hlsl_is_magic_counter_buffer = true; |
449 | 0 | break; |
450 | | |
451 | 0 | case DecorationFPRoundingMode: |
452 | 0 | dec.fp_rounding_mode = static_cast<FPRoundingMode>(argument); |
453 | 0 | break; |
454 | | |
455 | 0 | case DecorationFPFastMathMode: |
456 | 0 | dec.fp_fast_math_mode = static_cast<FPFastMathModeMask>(argument); |
457 | 0 | break; |
458 | | |
459 | 8.35k | default: |
460 | 8.35k | break; |
461 | 21.1k | } |
462 | 21.1k | } |
463 | | |
464 | | void ParsedIR::set_member_decoration(TypeID id, uint32_t index, Decoration decoration, uint32_t argument) |
465 | 20.1k | { |
466 | 20.1k | auto &m = meta[id]; |
467 | 20.1k | m.members.resize(max(m.members.size(), size_t(index) + 1)); |
468 | 20.1k | auto &dec = m.members[index]; |
469 | 20.1k | dec.decoration_flags.set(decoration); |
470 | | |
471 | 20.1k | switch (decoration) |
472 | 20.1k | { |
473 | 5.60k | case DecorationBuiltIn: |
474 | 5.60k | dec.builtin = true; |
475 | 5.60k | dec.builtin_type = static_cast<BuiltIn>(argument); |
476 | 5.60k | break; |
477 | | |
478 | 0 | case DecorationLocation: |
479 | 0 | dec.location = argument; |
480 | 0 | break; |
481 | | |
482 | 215 | case DecorationComponent: |
483 | 215 | dec.component = argument; |
484 | 215 | break; |
485 | | |
486 | 0 | case DecorationBinding: |
487 | 0 | dec.binding = argument; |
488 | 0 | break; |
489 | | |
490 | 386 | case DecorationOffset: |
491 | 386 | dec.offset = argument; |
492 | 386 | break; |
493 | | |
494 | 0 | case DecorationXfbBuffer: |
495 | 0 | dec.xfb_buffer = argument; |
496 | 0 | break; |
497 | | |
498 | 0 | case DecorationXfbStride: |
499 | 0 | dec.xfb_stride = argument; |
500 | 0 | break; |
501 | | |
502 | 0 | case DecorationStream: |
503 | 0 | dec.stream = argument; |
504 | 0 | break; |
505 | | |
506 | 2.58k | case DecorationSpecId: |
507 | 2.58k | dec.spec_id = argument; |
508 | 2.58k | break; |
509 | | |
510 | 0 | case DecorationMatrixStride: |
511 | 0 | dec.matrix_stride = argument; |
512 | 0 | break; |
513 | | |
514 | 0 | case DecorationIndex: |
515 | 0 | dec.index = argument; |
516 | 0 | break; |
517 | | |
518 | 11.3k | default: |
519 | 11.3k | break; |
520 | 20.1k | } |
521 | 20.1k | } |
522 | | |
523 | | // Recursively marks any constants referenced by the specified constant instruction as being used |
524 | | // as an array length. The id must be a constant instruction (SPIRConstant or SPIRConstantOp). |
525 | | void ParsedIR::mark_used_as_array_length(ID id) |
526 | 2.10k | { |
527 | 2.10k | switch (ids[id].get_type()) |
528 | 2.10k | { |
529 | 2.10k | case TypeConstant: |
530 | 2.10k | { |
531 | 2.10k | auto &c = get<SPIRConstant>(id); |
532 | 2.10k | c.is_used_as_array_length = true; |
533 | | |
534 | | // Mark composite dependencies as well. |
535 | 2.10k | for (auto &sub_id: c.m.id) |
536 | 8.40k | if (sub_id) |
537 | 0 | mark_used_as_array_length(sub_id); |
538 | | |
539 | 4.20k | for (uint32_t col = 0; col < c.m.columns; col++) |
540 | 2.10k | { |
541 | 2.10k | for (auto &sub_id : c.m.c[col].id) |
542 | 8.40k | if (sub_id) |
543 | 0 | mark_used_as_array_length(sub_id); |
544 | 2.10k | } |
545 | | |
546 | 2.10k | for (auto &sub_id : c.subconstants) |
547 | 0 | if (sub_id) |
548 | 0 | mark_used_as_array_length(sub_id); |
549 | 2.10k | break; |
550 | 0 | } |
551 | | |
552 | 1 | case TypeConstantOp: |
553 | 1 | { |
554 | 1 | auto &cop = get<SPIRConstantOp>(id); |
555 | 1 | if (cop.opcode == OpCompositeExtract) |
556 | 0 | mark_used_as_array_length(cop.arguments[0]); |
557 | 1 | else if (cop.opcode == OpCompositeInsert) |
558 | 0 | { |
559 | 0 | mark_used_as_array_length(cop.arguments[0]); |
560 | 0 | mark_used_as_array_length(cop.arguments[1]); |
561 | 0 | } |
562 | 1 | else |
563 | 1 | for (uint32_t arg_id : cop.arguments) |
564 | 0 | mark_used_as_array_length(arg_id); |
565 | 1 | break; |
566 | 0 | } |
567 | | |
568 | 0 | case TypeUndef: |
569 | 0 | break; |
570 | | |
571 | 0 | default: |
572 | 0 | assert(0); |
573 | 2.10k | } |
574 | 2.10k | } |
575 | | |
576 | | Bitset ParsedIR::get_buffer_block_type_flags(const SPIRType &type) const |
577 | 0 | { |
578 | 0 | if (type.member_types.empty()) |
579 | 0 | return {}; |
580 | | |
581 | 0 | Bitset all_members_flags = get_member_decoration_bitset(type.self, 0); |
582 | 0 | for (uint32_t i = 1; i < uint32_t(type.member_types.size()); i++) |
583 | 0 | all_members_flags.merge_and(get_member_decoration_bitset(type.self, i)); |
584 | 0 | return all_members_flags; |
585 | 0 | } |
586 | | |
587 | | Bitset ParsedIR::get_buffer_block_flags(const SPIRVariable &var) const |
588 | 0 | { |
589 | 0 | auto &type = get<SPIRType>(var.basetype); |
590 | 0 | if (type.basetype != SPIRType::Struct) |
591 | 0 | SPIRV_CROSS_THROW("Cannot get buffer block flags for non-buffer variable."); |
592 | | |
593 | | // Some flags like non-writable, non-readable are actually found |
594 | | // as member decorations. If all members have a decoration set, propagate |
595 | | // the decoration up as a regular variable decoration. |
596 | 0 | Bitset base_flags; |
597 | 0 | auto *m = find_meta(var.self); |
598 | 0 | if (m) |
599 | 0 | base_flags = m->decoration.decoration_flags; |
600 | |
|
601 | 0 | if (type.member_types.empty()) |
602 | 0 | return base_flags; |
603 | | |
604 | 0 | auto all_members_flags = get_buffer_block_type_flags(type); |
605 | 0 | base_flags.merge_or(all_members_flags); |
606 | 0 | return base_flags; |
607 | 0 | } |
608 | | |
609 | | const Bitset &ParsedIR::get_member_decoration_bitset(TypeID id, uint32_t index) const |
610 | 0 | { |
611 | 0 | auto *m = find_meta(id); |
612 | 0 | if (m) |
613 | 0 | { |
614 | 0 | if (index >= m->members.size()) |
615 | 0 | return cleared_bitset; |
616 | 0 | return m->members[index].decoration_flags; |
617 | 0 | } |
618 | 0 | else |
619 | 0 | return cleared_bitset; |
620 | 0 | } |
621 | | |
622 | | bool ParsedIR::has_decoration(ID id, Decoration decoration) const |
623 | 0 | { |
624 | 0 | return get_decoration_bitset(id).get(decoration); |
625 | 0 | } |
626 | | |
627 | | uint32_t ParsedIR::get_decoration(ID id, Decoration decoration) const |
628 | 19.8k | { |
629 | 19.8k | auto *m = find_meta(id); |
630 | 19.8k | if (!m) |
631 | 0 | return 0; |
632 | | |
633 | 19.8k | auto &dec = m->decoration; |
634 | 19.8k | if (!dec.decoration_flags.get(decoration)) |
635 | 0 | return 0; |
636 | | |
637 | 19.8k | switch (decoration) |
638 | 19.8k | { |
639 | 5.60k | case DecorationBuiltIn: |
640 | 5.60k | return dec.builtin_type; |
641 | 13 | case DecorationLocation: |
642 | 13 | return dec.location; |
643 | 13 | case DecorationComponent: |
644 | 13 | return dec.component; |
645 | 136 | case DecorationOffset: |
646 | 136 | return dec.offset; |
647 | 0 | case DecorationXfbBuffer: |
648 | 0 | return dec.xfb_buffer; |
649 | 0 | case DecorationXfbStride: |
650 | 0 | return dec.xfb_stride; |
651 | 0 | case DecorationStream: |
652 | 0 | return dec.stream; |
653 | 13 | case DecorationBinding: |
654 | 13 | return dec.binding; |
655 | 4.00k | case DecorationDescriptorSet: |
656 | 4.00k | return dec.set; |
657 | 0 | case DecorationInputAttachmentIndex: |
658 | 0 | return dec.input_attachment; |
659 | 2.59k | case DecorationSpecId: |
660 | 2.59k | return dec.spec_id; |
661 | 0 | case DecorationArrayStride: |
662 | 0 | return dec.array_stride; |
663 | 0 | case DecorationMatrixStride: |
664 | 0 | return dec.matrix_stride; |
665 | 8 | case DecorationIndex: |
666 | 8 | return dec.index; |
667 | 0 | case DecorationFPRoundingMode: |
668 | 0 | return dec.fp_rounding_mode; |
669 | 0 | case DecorationFPFastMathMode: |
670 | 0 | return dec.fp_fast_math_mode; |
671 | 7.45k | default: |
672 | 7.45k | return 1; |
673 | 19.8k | } |
674 | 19.8k | } |
675 | | |
676 | | const string &ParsedIR::get_decoration_string(ID id, Decoration decoration) const |
677 | 0 | { |
678 | 0 | auto *m = find_meta(id); |
679 | 0 | if (!m) |
680 | 0 | return empty_string; |
681 | | |
682 | 0 | auto &dec = m->decoration; |
683 | |
|
684 | 0 | if (!dec.decoration_flags.get(decoration)) |
685 | 0 | return empty_string; |
686 | | |
687 | 0 | switch (decoration) |
688 | 0 | { |
689 | 0 | case DecorationHlslSemanticGOOGLE: |
690 | 0 | return dec.hlsl_semantic; |
691 | | |
692 | 0 | case DecorationUserTypeGOOGLE: |
693 | 0 | return dec.user_type; |
694 | | |
695 | 0 | default: |
696 | 0 | return empty_string; |
697 | 0 | } |
698 | 0 | } |
699 | | |
700 | | void ParsedIR::unset_decoration(ID id, Decoration decoration) |
701 | 0 | { |
702 | 0 | auto &dec = meta[id].decoration; |
703 | 0 | dec.decoration_flags.clear(decoration); |
704 | 0 | switch (decoration) |
705 | 0 | { |
706 | 0 | case DecorationBuiltIn: |
707 | 0 | dec.builtin = false; |
708 | 0 | break; |
709 | | |
710 | 0 | case DecorationLocation: |
711 | 0 | dec.location = 0; |
712 | 0 | break; |
713 | | |
714 | 0 | case DecorationComponent: |
715 | 0 | dec.component = 0; |
716 | 0 | break; |
717 | | |
718 | 0 | case DecorationOffset: |
719 | 0 | dec.offset = 0; |
720 | 0 | break; |
721 | | |
722 | 0 | case DecorationXfbBuffer: |
723 | 0 | dec.xfb_buffer = 0; |
724 | 0 | break; |
725 | | |
726 | 0 | case DecorationXfbStride: |
727 | 0 | dec.xfb_stride = 0; |
728 | 0 | break; |
729 | | |
730 | 0 | case DecorationStream: |
731 | 0 | dec.stream = 0; |
732 | 0 | break; |
733 | | |
734 | 0 | case DecorationBinding: |
735 | 0 | dec.binding = 0; |
736 | 0 | break; |
737 | | |
738 | 0 | case DecorationDescriptorSet: |
739 | 0 | dec.set = 0; |
740 | 0 | break; |
741 | | |
742 | 0 | case DecorationInputAttachmentIndex: |
743 | 0 | dec.input_attachment = 0; |
744 | 0 | break; |
745 | | |
746 | 0 | case DecorationSpecId: |
747 | 0 | dec.spec_id = 0; |
748 | 0 | break; |
749 | | |
750 | 0 | case DecorationHlslSemanticGOOGLE: |
751 | 0 | dec.hlsl_semantic.clear(); |
752 | 0 | break; |
753 | | |
754 | 0 | case DecorationFPRoundingMode: |
755 | 0 | dec.fp_rounding_mode = FPRoundingModeMax; |
756 | 0 | break; |
757 | | |
758 | 0 | case DecorationFPFastMathMode: |
759 | 0 | dec.fp_fast_math_mode = FPFastMathModeMaskNone; |
760 | 0 | break; |
761 | | |
762 | 0 | case DecorationHlslCounterBufferGOOGLE: |
763 | 0 | { |
764 | 0 | auto &counter = meta[id].hlsl_magic_counter_buffer; |
765 | 0 | if (counter) |
766 | 0 | { |
767 | 0 | meta[counter].hlsl_is_magic_counter_buffer = false; |
768 | 0 | counter = 0; |
769 | 0 | } |
770 | 0 | break; |
771 | 0 | } |
772 | | |
773 | 0 | default: |
774 | 0 | break; |
775 | 0 | } |
776 | 0 | } |
777 | | |
778 | | bool ParsedIR::has_member_decoration(TypeID id, uint32_t index, Decoration decoration) const |
779 | 0 | { |
780 | 0 | return get_member_decoration_bitset(id, index).get(decoration); |
781 | 0 | } |
782 | | |
783 | | uint32_t ParsedIR::get_member_decoration(TypeID id, uint32_t index, Decoration decoration) const |
784 | 0 | { |
785 | 0 | auto *m = find_meta(id); |
786 | 0 | if (!m) |
787 | 0 | return 0; |
788 | | |
789 | 0 | if (index >= m->members.size()) |
790 | 0 | return 0; |
791 | | |
792 | 0 | auto &dec = m->members[index]; |
793 | 0 | if (!dec.decoration_flags.get(decoration)) |
794 | 0 | return 0; |
795 | | |
796 | 0 | switch (decoration) |
797 | 0 | { |
798 | 0 | case DecorationBuiltIn: |
799 | 0 | return dec.builtin_type; |
800 | 0 | case DecorationLocation: |
801 | 0 | return dec.location; |
802 | 0 | case DecorationComponent: |
803 | 0 | return dec.component; |
804 | 0 | case DecorationBinding: |
805 | 0 | return dec.binding; |
806 | 0 | case DecorationOffset: |
807 | 0 | return dec.offset; |
808 | 0 | case DecorationXfbBuffer: |
809 | 0 | return dec.xfb_buffer; |
810 | 0 | case DecorationXfbStride: |
811 | 0 | return dec.xfb_stride; |
812 | 0 | case DecorationStream: |
813 | 0 | return dec.stream; |
814 | 0 | case DecorationSpecId: |
815 | 0 | return dec.spec_id; |
816 | 0 | case DecorationMatrixStride: |
817 | 0 | return dec.matrix_stride; |
818 | 0 | case DecorationIndex: |
819 | 0 | return dec.index; |
820 | 0 | default: |
821 | 0 | return 1; |
822 | 0 | } |
823 | 0 | } |
824 | | |
825 | | const Bitset &ParsedIR::get_decoration_bitset(ID id) const |
826 | 0 | { |
827 | 0 | auto *m = find_meta(id); |
828 | 0 | if (m) |
829 | 0 | { |
830 | 0 | auto &dec = m->decoration; |
831 | 0 | return dec.decoration_flags; |
832 | 0 | } |
833 | 0 | else |
834 | 0 | return cleared_bitset; |
835 | 0 | } |
836 | | |
837 | | void ParsedIR::set_member_decoration_string(TypeID id, uint32_t index, Decoration decoration, const string &argument) |
838 | 0 | { |
839 | 0 | auto &m = meta[id]; |
840 | 0 | m.members.resize(max(m.members.size(), size_t(index) + 1)); |
841 | 0 | auto &dec = meta[id].members[index]; |
842 | 0 | dec.decoration_flags.set(decoration); |
843 | |
|
844 | 0 | switch (decoration) |
845 | 0 | { |
846 | 0 | case DecorationHlslSemanticGOOGLE: |
847 | 0 | dec.hlsl_semantic = argument; |
848 | 0 | break; |
849 | | |
850 | 0 | default: |
851 | 0 | break; |
852 | 0 | } |
853 | 0 | } |
854 | | |
855 | | const string &ParsedIR::get_member_decoration_string(TypeID id, uint32_t index, Decoration decoration) const |
856 | 0 | { |
857 | 0 | auto *m = find_meta(id); |
858 | 0 | if (m) |
859 | 0 | { |
860 | 0 | if (!has_member_decoration(id, index, decoration)) |
861 | 0 | return empty_string; |
862 | | |
863 | 0 | auto &dec = m->members[index]; |
864 | |
|
865 | 0 | switch (decoration) |
866 | 0 | { |
867 | 0 | case DecorationHlslSemanticGOOGLE: |
868 | 0 | return dec.hlsl_semantic; |
869 | | |
870 | 0 | default: |
871 | 0 | return empty_string; |
872 | 0 | } |
873 | 0 | } |
874 | 0 | else |
875 | 0 | return empty_string; |
876 | 0 | } |
877 | | |
878 | | void ParsedIR::unset_member_decoration(TypeID id, uint32_t index, Decoration decoration) |
879 | 0 | { |
880 | 0 | auto &m = meta[id]; |
881 | 0 | if (index >= m.members.size()) |
882 | 0 | return; |
883 | | |
884 | 0 | auto &dec = m.members[index]; |
885 | |
|
886 | 0 | dec.decoration_flags.clear(decoration); |
887 | 0 | switch (decoration) |
888 | 0 | { |
889 | 0 | case DecorationBuiltIn: |
890 | 0 | dec.builtin = false; |
891 | 0 | break; |
892 | | |
893 | 0 | case DecorationLocation: |
894 | 0 | dec.location = 0; |
895 | 0 | break; |
896 | | |
897 | 0 | case DecorationComponent: |
898 | 0 | dec.component = 0; |
899 | 0 | break; |
900 | | |
901 | 0 | case DecorationOffset: |
902 | 0 | dec.offset = 0; |
903 | 0 | break; |
904 | | |
905 | 0 | case DecorationXfbBuffer: |
906 | 0 | dec.xfb_buffer = 0; |
907 | 0 | break; |
908 | | |
909 | 0 | case DecorationXfbStride: |
910 | 0 | dec.xfb_stride = 0; |
911 | 0 | break; |
912 | | |
913 | 0 | case DecorationStream: |
914 | 0 | dec.stream = 0; |
915 | 0 | break; |
916 | | |
917 | 0 | case DecorationSpecId: |
918 | 0 | dec.spec_id = 0; |
919 | 0 | break; |
920 | | |
921 | 0 | case DecorationHlslSemanticGOOGLE: |
922 | 0 | dec.hlsl_semantic.clear(); |
923 | 0 | break; |
924 | | |
925 | 0 | default: |
926 | 0 | break; |
927 | 0 | } |
928 | 0 | } |
929 | | |
930 | | uint32_t ParsedIR::increase_bound_by(uint32_t incr_amount) |
931 | 41.8k | { |
932 | 41.8k | auto curr_bound = ids.size(); |
933 | 41.8k | auto new_bound = curr_bound + incr_amount; |
934 | | |
935 | 41.8k | ids.reserve(ids.size() + incr_amount); |
936 | 85.4k | for (uint32_t i = 0; i < incr_amount; i++) |
937 | 43.6k | ids.emplace_back(pool_group.get()); |
938 | | |
939 | 41.8k | block_meta.resize(new_bound); |
940 | 41.8k | return uint32_t(curr_bound); |
941 | 41.8k | } |
942 | | |
943 | | void ParsedIR::remove_typed_id(Types type, ID id) |
944 | 7 | { |
945 | 7 | auto &type_ids = ids_for_type[type]; |
946 | 7 | type_ids.erase(remove(begin(type_ids), end(type_ids), id), end(type_ids)); |
947 | 7 | } |
948 | | |
949 | | void ParsedIR::reset_all_of_type(Types type) |
950 | 0 | { |
951 | 0 | for (auto &id : ids_for_type[type]) |
952 | 0 | if (ids[id].get_type() == type) |
953 | 0 | ids[id].reset(); |
954 | |
|
955 | 0 | ids_for_type[type].clear(); |
956 | 0 | } |
957 | | |
958 | | void ParsedIR::add_typed_id(Types type, ID id) |
959 | 344k | { |
960 | 344k | assert(id < ids.size()); |
961 | | |
962 | 344k | if (loop_iteration_depth_hard != 0) |
963 | 0 | SPIRV_CROSS_THROW("Cannot add typed ID while looping over it."); |
964 | | |
965 | 344k | if (loop_iteration_depth_soft != 0) |
966 | 0 | { |
967 | 0 | if (!ids[id].empty()) |
968 | 0 | SPIRV_CROSS_THROW("Cannot override IDs when loop is soft locked."); |
969 | 0 | return; |
970 | 0 | } |
971 | | |
972 | 344k | if (ids[id].empty() || ids[id].get_type() != type) |
973 | 55.4k | { |
974 | 55.4k | switch (type) |
975 | 55.4k | { |
976 | 46.1k | case TypeConstant: |
977 | 46.1k | ids_for_constant_or_variable.push_back(id); |
978 | 46.1k | ids_for_constant_undef_or_type.push_back(id); |
979 | 46.1k | break; |
980 | | |
981 | 2.29k | case TypeVariable: |
982 | 2.29k | ids_for_constant_or_variable.push_back(id); |
983 | 2.29k | break; |
984 | | |
985 | 2.15k | case TypeType: |
986 | 2.15k | case TypeConstantOp: |
987 | 2.17k | case TypeUndef: |
988 | 2.17k | ids_for_constant_undef_or_type.push_back(id); |
989 | 2.17k | break; |
990 | | |
991 | 4.79k | default: |
992 | 4.79k | break; |
993 | 55.4k | } |
994 | 55.4k | } |
995 | | |
996 | 344k | if (ids[id].empty()) |
997 | 55.4k | { |
998 | 55.4k | ids_for_type[type].push_back(id); |
999 | 55.4k | } |
1000 | 288k | else if (ids[id].get_type() != type) |
1001 | 7 | { |
1002 | 7 | remove_typed_id(ids[id].get_type(), id); |
1003 | 7 | ids_for_type[type].push_back(id); |
1004 | 7 | } |
1005 | 344k | } |
1006 | | |
1007 | | const Meta *ParsedIR::find_meta(ID id) const |
1008 | 25.4k | { |
1009 | 25.4k | auto itr = meta.find(id); |
1010 | 25.4k | if (itr != end(meta)) |
1011 | 23.4k | return &itr->second; |
1012 | 2.01k | else |
1013 | 2.01k | return nullptr; |
1014 | 25.4k | } |
1015 | | |
1016 | | Meta *ParsedIR::find_meta(ID id) |
1017 | 0 | { |
1018 | 0 | auto itr = meta.find(id); |
1019 | 0 | if (itr != end(meta)) |
1020 | 0 | return &itr->second; |
1021 | 0 | else |
1022 | 0 | return nullptr; |
1023 | 0 | } |
1024 | | |
1025 | | ParsedIR::LoopLock ParsedIR::create_loop_hard_lock() const |
1026 | 0 | { |
1027 | 0 | return ParsedIR::LoopLock(&loop_iteration_depth_hard); |
1028 | 0 | } |
1029 | | |
1030 | | ParsedIR::LoopLock ParsedIR::create_loop_soft_lock() const |
1031 | 0 | { |
1032 | 0 | return ParsedIR::LoopLock(&loop_iteration_depth_soft); |
1033 | 0 | } |
1034 | | |
1035 | | ParsedIR::LoopLock::~LoopLock() |
1036 | 0 | { |
1037 | 0 | if (lock) |
1038 | 0 | (*lock)--; |
1039 | 0 | } |
1040 | | |
1041 | | ParsedIR::LoopLock::LoopLock(uint32_t *lock_) |
1042 | 0 | : lock(lock_) |
1043 | 0 | { |
1044 | 0 | if (lock) |
1045 | 0 | (*lock)++; |
1046 | 0 | } |
1047 | | |
1048 | | ParsedIR::LoopLock::LoopLock(LoopLock &&other) SPIRV_CROSS_NOEXCEPT |
1049 | 0 | { |
1050 | 0 | *this = std::move(other); |
1051 | 0 | } |
1052 | | |
1053 | | ParsedIR::LoopLock &ParsedIR::LoopLock::operator=(LoopLock &&other) SPIRV_CROSS_NOEXCEPT |
1054 | 0 | { |
1055 | 0 | if (lock) |
1056 | 0 | (*lock)--; |
1057 | 0 | lock = other.lock; |
1058 | 0 | other.lock = nullptr; |
1059 | 0 | return *this; |
1060 | 0 | } |
1061 | | |
1062 | | void ParsedIR::make_constant_null(uint32_t id, uint32_t type, bool add_to_typed_id_set) |
1063 | 85.4k | { |
1064 | 85.4k | assert(id < ids.size()); |
1065 | | |
1066 | 85.4k | auto &constant_type = get<SPIRType>(type); |
1067 | | |
1068 | 85.4k | if (constant_type.pointer) |
1069 | 0 | { |
1070 | 0 | if (add_to_typed_id_set) |
1071 | 0 | add_typed_id(TypeConstant, id); |
1072 | 0 | auto &constant = variant_set<SPIRConstant>(ids[id], type); |
1073 | 0 | constant.self = id; |
1074 | 0 | constant.make_null(constant_type); |
1075 | 0 | } |
1076 | 85.4k | else if (!constant_type.array.empty()) |
1077 | 40.9k | { |
1078 | 40.9k | assert(constant_type.parent_type); |
1079 | 40.9k | uint32_t parent_id = increase_bound_by(1); |
1080 | 40.9k | make_constant_null(parent_id, constant_type.parent_type, add_to_typed_id_set); |
1081 | | |
1082 | | // The array size of OpConstantNull can be either literal or specialization constant. |
1083 | | // In the latter case, we cannot take the value as-is, as it can be changed to anything. |
1084 | | // Rather, we assume it to be *one* for the sake of initializer. |
1085 | 40.9k | bool is_literal_array_size = constant_type.array_size_literal.back(); |
1086 | 40.9k | uint32_t count = is_literal_array_size ? constant_type.array.back() : 1; |
1087 | | |
1088 | 40.9k | SmallVector<uint32_t> elements(count); |
1089 | 50.1M | for (uint32_t i = 0; i < count; i++) |
1090 | 50.1M | elements[i] = parent_id; |
1091 | | |
1092 | 40.9k | if (add_to_typed_id_set) |
1093 | 40.9k | add_typed_id(TypeConstant, id); |
1094 | 40.9k | auto& constant = variant_set<SPIRConstant>(ids[id], type, elements.data(), uint32_t(elements.size()), false); |
1095 | 40.9k | constant.self = id; |
1096 | 40.9k | constant.is_null_array_specialized_length = !is_literal_array_size; |
1097 | 40.9k | } |
1098 | 44.4k | else if (!constant_type.member_types.empty()) |
1099 | 871 | { |
1100 | 871 | uint32_t member_ids = increase_bound_by(uint32_t(constant_type.member_types.size())); |
1101 | 871 | SmallVector<uint32_t> elements(constant_type.member_types.size()); |
1102 | 3.48k | for (uint32_t i = 0; i < constant_type.member_types.size(); i++) |
1103 | 2.61k | { |
1104 | 2.61k | make_constant_null(member_ids + i, constant_type.member_types[i], add_to_typed_id_set); |
1105 | 2.61k | elements[i] = member_ids + i; |
1106 | 2.61k | } |
1107 | | |
1108 | 871 | if (add_to_typed_id_set) |
1109 | 871 | add_typed_id(TypeConstant, id); |
1110 | 871 | variant_set<SPIRConstant>(ids[id], type, elements.data(), uint32_t(elements.size()), false).self = id; |
1111 | 871 | } |
1112 | 43.6k | else |
1113 | 43.6k | { |
1114 | 43.6k | if (add_to_typed_id_set) |
1115 | 43.6k | add_typed_id(TypeConstant, id); |
1116 | 43.6k | auto &constant = variant_set<SPIRConstant>(ids[id], type); |
1117 | 43.6k | constant.self = id; |
1118 | 43.6k | constant.make_null(constant_type); |
1119 | 43.6k | } |
1120 | 85.4k | } |
1121 | | |
1122 | | } // namespace SPIRV_CROSS_NAMESPACE |