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

source code of qtshadertools/src/3rdparty/SPIRV-Cross/spirv_cross_parsed_ir.cpp