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 assert(type.basetype == SPIRType::Struct);
568
569 // Some flags like non-writable, non-readable are actually found
570 // as member decorations. If all members have a decoration set, propagate
571 // the decoration up as a regular variable decoration.
572 Bitset base_flags;
573 auto *m = find_meta(id: var.self);
574 if (m)
575 base_flags = m->decoration.decoration_flags;
576
577 if (type.member_types.empty())
578 return base_flags;
579
580 auto all_members_flags = get_buffer_block_type_flags(type);
581 base_flags.merge_or(other: all_members_flags);
582 return base_flags;
583}
584
585const Bitset &ParsedIR::get_member_decoration_bitset(TypeID id, uint32_t index) const
586{
587 auto *m = find_meta(id);
588 if (m)
589 {
590 if (index >= m->members.size())
591 return cleared_bitset;
592 return m->members[index].decoration_flags;
593 }
594 else
595 return cleared_bitset;
596}
597
598bool ParsedIR::has_decoration(ID id, Decoration decoration) const
599{
600 return get_decoration_bitset(id).get(bit: decoration);
601}
602
603uint32_t ParsedIR::get_decoration(ID id, Decoration decoration) const
604{
605 auto *m = find_meta(id);
606 if (!m)
607 return 0;
608
609 auto &dec = m->decoration;
610 if (!dec.decoration_flags.get(bit: decoration))
611 return 0;
612
613 switch (decoration)
614 {
615 case DecorationBuiltIn:
616 return dec.builtin_type;
617 case DecorationLocation:
618 return dec.location;
619 case DecorationComponent:
620 return dec.component;
621 case DecorationOffset:
622 return dec.offset;
623 case DecorationXfbBuffer:
624 return dec.xfb_buffer;
625 case DecorationXfbStride:
626 return dec.xfb_stride;
627 case DecorationStream:
628 return dec.stream;
629 case DecorationBinding:
630 return dec.binding;
631 case DecorationDescriptorSet:
632 return dec.set;
633 case DecorationInputAttachmentIndex:
634 return dec.input_attachment;
635 case DecorationSpecId:
636 return dec.spec_id;
637 case DecorationArrayStride:
638 return dec.array_stride;
639 case DecorationMatrixStride:
640 return dec.matrix_stride;
641 case DecorationIndex:
642 return dec.index;
643 case DecorationFPRoundingMode:
644 return dec.fp_rounding_mode;
645 default:
646 return 1;
647 }
648}
649
650const string &ParsedIR::get_decoration_string(ID id, Decoration decoration) const
651{
652 auto *m = find_meta(id);
653 if (!m)
654 return empty_string;
655
656 auto &dec = m->decoration;
657
658 if (!dec.decoration_flags.get(bit: decoration))
659 return empty_string;
660
661 switch (decoration)
662 {
663 case DecorationHlslSemanticGOOGLE:
664 return dec.hlsl_semantic;
665
666 case DecorationUserTypeGOOGLE:
667 return dec.user_type;
668
669 default:
670 return empty_string;
671 }
672}
673
674void ParsedIR::unset_decoration(ID id, Decoration decoration)
675{
676 auto &dec = meta[id].decoration;
677 dec.decoration_flags.clear(bit: decoration);
678 switch (decoration)
679 {
680 case DecorationBuiltIn:
681 dec.builtin = false;
682 break;
683
684 case DecorationLocation:
685 dec.location = 0;
686 break;
687
688 case DecorationComponent:
689 dec.component = 0;
690 break;
691
692 case DecorationOffset:
693 dec.offset = 0;
694 break;
695
696 case DecorationXfbBuffer:
697 dec.xfb_buffer = 0;
698 break;
699
700 case DecorationXfbStride:
701 dec.xfb_stride = 0;
702 break;
703
704 case DecorationStream:
705 dec.stream = 0;
706 break;
707
708 case DecorationBinding:
709 dec.binding = 0;
710 break;
711
712 case DecorationDescriptorSet:
713 dec.set = 0;
714 break;
715
716 case DecorationInputAttachmentIndex:
717 dec.input_attachment = 0;
718 break;
719
720 case DecorationSpecId:
721 dec.spec_id = 0;
722 break;
723
724 case DecorationHlslSemanticGOOGLE:
725 dec.hlsl_semantic.clear();
726 break;
727
728 case DecorationFPRoundingMode:
729 dec.fp_rounding_mode = FPRoundingModeMax;
730 break;
731
732 case DecorationHlslCounterBufferGOOGLE:
733 {
734 auto &counter = meta[id].hlsl_magic_counter_buffer;
735 if (counter)
736 {
737 meta[counter].hlsl_is_magic_counter_buffer = false;
738 counter = 0;
739 }
740 break;
741 }
742
743 default:
744 break;
745 }
746}
747
748bool ParsedIR::has_member_decoration(TypeID id, uint32_t index, Decoration decoration) const
749{
750 return get_member_decoration_bitset(id, index).get(bit: decoration);
751}
752
753uint32_t ParsedIR::get_member_decoration(TypeID id, uint32_t index, Decoration decoration) const
754{
755 auto *m = find_meta(id);
756 if (!m)
757 return 0;
758
759 if (index >= m->members.size())
760 return 0;
761
762 auto &dec = m->members[index];
763 if (!dec.decoration_flags.get(bit: decoration))
764 return 0;
765
766 switch (decoration)
767 {
768 case DecorationBuiltIn:
769 return dec.builtin_type;
770 case DecorationLocation:
771 return dec.location;
772 case DecorationComponent:
773 return dec.component;
774 case DecorationBinding:
775 return dec.binding;
776 case DecorationOffset:
777 return dec.offset;
778 case DecorationXfbBuffer:
779 return dec.xfb_buffer;
780 case DecorationXfbStride:
781 return dec.xfb_stride;
782 case DecorationStream:
783 return dec.stream;
784 case DecorationSpecId:
785 return dec.spec_id;
786 case DecorationIndex:
787 return dec.index;
788 default:
789 return 1;
790 }
791}
792
793const Bitset &ParsedIR::get_decoration_bitset(ID id) const
794{
795 auto *m = find_meta(id);
796 if (m)
797 {
798 auto &dec = m->decoration;
799 return dec.decoration_flags;
800 }
801 else
802 return cleared_bitset;
803}
804
805void ParsedIR::set_member_decoration_string(TypeID id, uint32_t index, Decoration decoration, const string &argument)
806{
807 auto &m = meta[id];
808 m.members.resize(new_size: max(a: m.members.size(), b: size_t(index) + 1));
809 auto &dec = meta[id].members[index];
810 dec.decoration_flags.set(decoration);
811
812 switch (decoration)
813 {
814 case DecorationHlslSemanticGOOGLE:
815 dec.hlsl_semantic = argument;
816 break;
817
818 default:
819 break;
820 }
821}
822
823const string &ParsedIR::get_member_decoration_string(TypeID id, uint32_t index, Decoration decoration) const
824{
825 auto *m = find_meta(id);
826 if (m)
827 {
828 if (!has_member_decoration(id, index, decoration))
829 return empty_string;
830
831 auto &dec = m->members[index];
832
833 switch (decoration)
834 {
835 case DecorationHlslSemanticGOOGLE:
836 return dec.hlsl_semantic;
837
838 default:
839 return empty_string;
840 }
841 }
842 else
843 return empty_string;
844}
845
846void ParsedIR::unset_member_decoration(TypeID id, uint32_t index, Decoration decoration)
847{
848 auto &m = meta[id];
849 if (index >= m.members.size())
850 return;
851
852 auto &dec = m.members[index];
853
854 dec.decoration_flags.clear(bit: decoration);
855 switch (decoration)
856 {
857 case DecorationBuiltIn:
858 dec.builtin = false;
859 break;
860
861 case DecorationLocation:
862 dec.location = 0;
863 break;
864
865 case DecorationComponent:
866 dec.component = 0;
867 break;
868
869 case DecorationOffset:
870 dec.offset = 0;
871 break;
872
873 case DecorationXfbBuffer:
874 dec.xfb_buffer = 0;
875 break;
876
877 case DecorationXfbStride:
878 dec.xfb_stride = 0;
879 break;
880
881 case DecorationStream:
882 dec.stream = 0;
883 break;
884
885 case DecorationSpecId:
886 dec.spec_id = 0;
887 break;
888
889 case DecorationHlslSemanticGOOGLE:
890 dec.hlsl_semantic.clear();
891 break;
892
893 default:
894 break;
895 }
896}
897
898uint32_t ParsedIR::increase_bound_by(uint32_t incr_amount)
899{
900 auto curr_bound = ids.size();
901 auto new_bound = curr_bound + incr_amount;
902
903 ids.reserve(count: ids.size() + incr_amount);
904 for (uint32_t i = 0; i < incr_amount; i++)
905 ids.emplace_back(ts: pool_group.get());
906
907 block_meta.resize(new_size: new_bound);
908 return uint32_t(curr_bound);
909}
910
911void ParsedIR::remove_typed_id(Types type, ID id)
912{
913 auto &type_ids = ids_for_type[type];
914 type_ids.erase(start_erase: remove(first: begin(cont&: type_ids), last: end(cont&: type_ids), value: id), end_erase: end(cont&: type_ids));
915}
916
917void ParsedIR::reset_all_of_type(Types type)
918{
919 for (auto &id : ids_for_type[type])
920 if (ids[id].get_type() == type)
921 ids[id].reset();
922
923 ids_for_type[type].clear();
924}
925
926void ParsedIR::add_typed_id(Types type, ID id)
927{
928 if (loop_iteration_depth_hard != 0)
929 SPIRV_CROSS_THROW("Cannot add typed ID while looping over it.");
930
931 if (loop_iteration_depth_soft != 0)
932 {
933 if (!ids[id].empty())
934 SPIRV_CROSS_THROW("Cannot override IDs when loop is soft locked.");
935 return;
936 }
937
938 if (ids[id].empty() || ids[id].get_type() != type)
939 {
940 switch (type)
941 {
942 case TypeConstant:
943 ids_for_constant_or_variable.push_back(t: id);
944 ids_for_constant_undef_or_type.push_back(t: id);
945 break;
946
947 case TypeVariable:
948 ids_for_constant_or_variable.push_back(t: id);
949 break;
950
951 case TypeType:
952 case TypeConstantOp:
953 case TypeUndef:
954 ids_for_constant_undef_or_type.push_back(t: id);
955 break;
956
957 default:
958 break;
959 }
960 }
961
962 if (ids[id].empty())
963 {
964 ids_for_type[type].push_back(t: id);
965 }
966 else if (ids[id].get_type() != type)
967 {
968 remove_typed_id(type: ids[id].get_type(), id);
969 ids_for_type[type].push_back(t: id);
970 }
971}
972
973const Meta *ParsedIR::find_meta(ID id) const
974{
975 auto itr = meta.find(x: id);
976 if (itr != end(cont: meta))
977 return &itr->second;
978 else
979 return nullptr;
980}
981
982Meta *ParsedIR::find_meta(ID id)
983{
984 auto itr = meta.find(x: id);
985 if (itr != end(cont&: meta))
986 return &itr->second;
987 else
988 return nullptr;
989}
990
991ParsedIR::LoopLock ParsedIR::create_loop_hard_lock() const
992{
993 return ParsedIR::LoopLock(&loop_iteration_depth_hard);
994}
995
996ParsedIR::LoopLock ParsedIR::create_loop_soft_lock() const
997{
998 return ParsedIR::LoopLock(&loop_iteration_depth_soft);
999}
1000
1001ParsedIR::LoopLock::~LoopLock()
1002{
1003 if (lock)
1004 (*lock)--;
1005}
1006
1007ParsedIR::LoopLock::LoopLock(uint32_t *lock_)
1008 : lock(lock_)
1009{
1010 if (lock)
1011 (*lock)++;
1012}
1013
1014ParsedIR::LoopLock::LoopLock(LoopLock &&other) SPIRV_CROSS_NOEXCEPT
1015{
1016 *this = std::move(other);
1017}
1018
1019ParsedIR::LoopLock &ParsedIR::LoopLock::operator=(LoopLock &&other) SPIRV_CROSS_NOEXCEPT
1020{
1021 if (lock)
1022 (*lock)--;
1023 lock = other.lock;
1024 other.lock = nullptr;
1025 return *this;
1026}
1027
1028void ParsedIR::make_constant_null(uint32_t id, uint32_t type, bool add_to_typed_id_set)
1029{
1030 auto &constant_type = get<SPIRType>(id: type);
1031
1032 if (constant_type.pointer)
1033 {
1034 if (add_to_typed_id_set)
1035 add_typed_id(type: TypeConstant, id);
1036 auto &constant = variant_set<SPIRConstant>(var&: ids[id], args&: type);
1037 constant.self = id;
1038 constant.make_null(constant_type_: constant_type);
1039 }
1040 else if (!constant_type.array.empty())
1041 {
1042 assert(constant_type.parent_type);
1043 uint32_t parent_id = increase_bound_by(incr_amount: 1);
1044 make_constant_null(id: parent_id, type: constant_type.parent_type, add_to_typed_id_set);
1045
1046 if (!constant_type.array_size_literal.back())
1047 SPIRV_CROSS_THROW("Array size of OpConstantNull must be a literal.");
1048
1049 SmallVector<uint32_t> elements(constant_type.array.back());
1050 for (uint32_t i = 0; i < constant_type.array.back(); i++)
1051 elements[i] = parent_id;
1052
1053 if (add_to_typed_id_set)
1054 add_typed_id(type: TypeConstant, id);
1055 variant_set<SPIRConstant>(var&: ids[id], args&: type, args: elements.data(), args: uint32_t(elements.size()), args: false).self = id;
1056 }
1057 else if (!constant_type.member_types.empty())
1058 {
1059 uint32_t member_ids = increase_bound_by(incr_amount: uint32_t(constant_type.member_types.size()));
1060 SmallVector<uint32_t> elements(constant_type.member_types.size());
1061 for (uint32_t i = 0; i < constant_type.member_types.size(); i++)
1062 {
1063 make_constant_null(id: member_ids + i, type: constant_type.member_types[i], add_to_typed_id_set);
1064 elements[i] = member_ids + i;
1065 }
1066
1067 if (add_to_typed_id_set)
1068 add_typed_id(type: TypeConstant, id);
1069 variant_set<SPIRConstant>(var&: ids[id], args&: type, args: elements.data(), args: uint32_t(elements.size()), args: false).self = id;
1070 }
1071 else
1072 {
1073 if (add_to_typed_id_set)
1074 add_typed_id(type: TypeConstant, id);
1075 auto &constant = variant_set<SPIRConstant>(var&: ids[id], args&: type);
1076 constant.self = id;
1077 constant.make_null(constant_type_: constant_type);
1078 }
1079}
1080
1081} // namespace SPIRV_CROSS_NAMESPACE
1082

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