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_or_type = std::move(other.ids_for_constant_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_or_type = other.ids_for_constant_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 auto &m = meta[id];
334 sanitize_identifier(name&: m.decoration.alias, member: false, allow_reserved_prefixes: false);
335 for (auto &memb : m.members)
336 sanitize_identifier(name&: memb.alias, member: true, allow_reserved_prefixes: false);
337 }
338 meta_needing_name_fixup.clear();
339}
340
341void ParsedIR::set_name(ID id, const string &name)
342{
343 auto &m = meta[id];
344 m.decoration.alias = name;
345 if (!is_valid_identifier(name) || is_reserved_identifier(name, member: false, allow_reserved_prefixes: false))
346 meta_needing_name_fixup.insert(x: id);
347}
348
349void ParsedIR::set_member_name(TypeID id, uint32_t index, const string &name)
350{
351 auto &m = meta[id];
352 m.members.resize(new_size: max(a: m.members.size(), b: size_t(index) + 1));
353 m.members[index].alias = name;
354 if (!is_valid_identifier(name) || is_reserved_identifier(name, member: true, allow_reserved_prefixes: false))
355 meta_needing_name_fixup.insert(x: id);
356}
357
358void ParsedIR::set_decoration_string(ID id, Decoration decoration, const string &argument)
359{
360 auto &dec = meta[id].decoration;
361 dec.decoration_flags.set(decoration);
362
363 switch (decoration)
364 {
365 case DecorationHlslSemanticGOOGLE:
366 dec.hlsl_semantic = argument;
367 break;
368
369 default:
370 break;
371 }
372}
373
374void ParsedIR::set_decoration(ID id, Decoration decoration, uint32_t argument)
375{
376 auto &dec = meta[id].decoration;
377 dec.decoration_flags.set(decoration);
378
379 switch (decoration)
380 {
381 case DecorationBuiltIn:
382 dec.builtin = true;
383 dec.builtin_type = static_cast<BuiltIn>(argument);
384 break;
385
386 case DecorationLocation:
387 dec.location = argument;
388 break;
389
390 case DecorationComponent:
391 dec.component = argument;
392 break;
393
394 case DecorationOffset:
395 dec.offset = argument;
396 break;
397
398 case DecorationXfbBuffer:
399 dec.xfb_buffer = argument;
400 break;
401
402 case DecorationXfbStride:
403 dec.xfb_stride = argument;
404 break;
405
406 case DecorationStream:
407 dec.stream = argument;
408 break;
409
410 case DecorationArrayStride:
411 dec.array_stride = argument;
412 break;
413
414 case DecorationMatrixStride:
415 dec.matrix_stride = argument;
416 break;
417
418 case DecorationBinding:
419 dec.binding = argument;
420 break;
421
422 case DecorationDescriptorSet:
423 dec.set = argument;
424 break;
425
426 case DecorationInputAttachmentIndex:
427 dec.input_attachment = argument;
428 break;
429
430 case DecorationSpecId:
431 dec.spec_id = argument;
432 break;
433
434 case DecorationIndex:
435 dec.index = argument;
436 break;
437
438 case DecorationHlslCounterBufferGOOGLE:
439 meta[id].hlsl_magic_counter_buffer = argument;
440 meta[argument].hlsl_is_magic_counter_buffer = true;
441 break;
442
443 case DecorationFPRoundingMode:
444 dec.fp_rounding_mode = static_cast<FPRoundingMode>(argument);
445 break;
446
447 default:
448 break;
449 }
450}
451
452void ParsedIR::set_member_decoration(TypeID id, uint32_t index, Decoration decoration, uint32_t argument)
453{
454 auto &m = meta[id];
455 m.members.resize(new_size: max(a: m.members.size(), b: size_t(index) + 1));
456 auto &dec = m.members[index];
457 dec.decoration_flags.set(decoration);
458
459 switch (decoration)
460 {
461 case DecorationBuiltIn:
462 dec.builtin = true;
463 dec.builtin_type = static_cast<BuiltIn>(argument);
464 break;
465
466 case DecorationLocation:
467 dec.location = argument;
468 break;
469
470 case DecorationComponent:
471 dec.component = argument;
472 break;
473
474 case DecorationBinding:
475 dec.binding = argument;
476 break;
477
478 case DecorationOffset:
479 dec.offset = argument;
480 break;
481
482 case DecorationXfbBuffer:
483 dec.xfb_buffer = argument;
484 break;
485
486 case DecorationXfbStride:
487 dec.xfb_stride = argument;
488 break;
489
490 case DecorationStream:
491 dec.stream = argument;
492 break;
493
494 case DecorationSpecId:
495 dec.spec_id = argument;
496 break;
497
498 case DecorationMatrixStride:
499 dec.matrix_stride = argument;
500 break;
501
502 case DecorationIndex:
503 dec.index = argument;
504 break;
505
506 default:
507 break;
508 }
509}
510
511// Recursively marks any constants referenced by the specified constant instruction as being used
512// as an array length. The id must be a constant instruction (SPIRConstant or SPIRConstantOp).
513void ParsedIR::mark_used_as_array_length(ID id)
514{
515 switch (ids[id].get_type())
516 {
517 case TypeConstant:
518 get<SPIRConstant>(id).is_used_as_array_length = true;
519 break;
520
521 case TypeConstantOp:
522 {
523 auto &cop = get<SPIRConstantOp>(id);
524 if (cop.opcode == OpCompositeExtract)
525 mark_used_as_array_length(id: cop.arguments[0]);
526 else if (cop.opcode == OpCompositeInsert)
527 {
528 mark_used_as_array_length(id: cop.arguments[0]);
529 mark_used_as_array_length(id: cop.arguments[1]);
530 }
531 else
532 for (uint32_t arg_id : cop.arguments)
533 mark_used_as_array_length(id: arg_id);
534 break;
535 }
536
537 case TypeUndef:
538 break;
539
540 default:
541 assert(0);
542 }
543}
544
545Bitset ParsedIR::get_buffer_block_type_flags(const SPIRType &type) const
546{
547 if (type.member_types.empty())
548 return {};
549
550 Bitset all_members_flags = get_member_decoration_bitset(id: type.self, index: 0);
551 for (uint32_t i = 1; i < uint32_t(type.member_types.size()); i++)
552 all_members_flags.merge_and(other: get_member_decoration_bitset(id: type.self, index: i));
553 return all_members_flags;
554}
555
556Bitset ParsedIR::get_buffer_block_flags(const SPIRVariable &var) const
557{
558 auto &type = get<SPIRType>(id: var.basetype);
559 assert(type.basetype == SPIRType::Struct);
560
561 // Some flags like non-writable, non-readable are actually found
562 // as member decorations. If all members have a decoration set, propagate
563 // the decoration up as a regular variable decoration.
564 Bitset base_flags;
565 auto *m = find_meta(id: var.self);
566 if (m)
567 base_flags = m->decoration.decoration_flags;
568
569 if (type.member_types.empty())
570 return base_flags;
571
572 auto all_members_flags = get_buffer_block_type_flags(type);
573 base_flags.merge_or(other: all_members_flags);
574 return base_flags;
575}
576
577const Bitset &ParsedIR::get_member_decoration_bitset(TypeID id, uint32_t index) const
578{
579 auto *m = find_meta(id);
580 if (m)
581 {
582 if (index >= m->members.size())
583 return cleared_bitset;
584 return m->members[index].decoration_flags;
585 }
586 else
587 return cleared_bitset;
588}
589
590bool ParsedIR::has_decoration(ID id, Decoration decoration) const
591{
592 return get_decoration_bitset(id).get(bit: decoration);
593}
594
595uint32_t ParsedIR::get_decoration(ID id, Decoration decoration) const
596{
597 auto *m = find_meta(id);
598 if (!m)
599 return 0;
600
601 auto &dec = m->decoration;
602 if (!dec.decoration_flags.get(bit: decoration))
603 return 0;
604
605 switch (decoration)
606 {
607 case DecorationBuiltIn:
608 return dec.builtin_type;
609 case DecorationLocation:
610 return dec.location;
611 case DecorationComponent:
612 return dec.component;
613 case DecorationOffset:
614 return dec.offset;
615 case DecorationXfbBuffer:
616 return dec.xfb_buffer;
617 case DecorationXfbStride:
618 return dec.xfb_stride;
619 case DecorationStream:
620 return dec.stream;
621 case DecorationBinding:
622 return dec.binding;
623 case DecorationDescriptorSet:
624 return dec.set;
625 case DecorationInputAttachmentIndex:
626 return dec.input_attachment;
627 case DecorationSpecId:
628 return dec.spec_id;
629 case DecorationArrayStride:
630 return dec.array_stride;
631 case DecorationMatrixStride:
632 return dec.matrix_stride;
633 case DecorationIndex:
634 return dec.index;
635 case DecorationFPRoundingMode:
636 return dec.fp_rounding_mode;
637 default:
638 return 1;
639 }
640}
641
642const string &ParsedIR::get_decoration_string(ID id, Decoration decoration) const
643{
644 auto *m = find_meta(id);
645 if (!m)
646 return empty_string;
647
648 auto &dec = m->decoration;
649
650 if (!dec.decoration_flags.get(bit: decoration))
651 return empty_string;
652
653 switch (decoration)
654 {
655 case DecorationHlslSemanticGOOGLE:
656 return dec.hlsl_semantic;
657
658 default:
659 return empty_string;
660 }
661}
662
663void ParsedIR::unset_decoration(ID id, Decoration decoration)
664{
665 auto &dec = meta[id].decoration;
666 dec.decoration_flags.clear(bit: decoration);
667 switch (decoration)
668 {
669 case DecorationBuiltIn:
670 dec.builtin = false;
671 break;
672
673 case DecorationLocation:
674 dec.location = 0;
675 break;
676
677 case DecorationComponent:
678 dec.component = 0;
679 break;
680
681 case DecorationOffset:
682 dec.offset = 0;
683 break;
684
685 case DecorationXfbBuffer:
686 dec.xfb_buffer = 0;
687 break;
688
689 case DecorationXfbStride:
690 dec.xfb_stride = 0;
691 break;
692
693 case DecorationStream:
694 dec.stream = 0;
695 break;
696
697 case DecorationBinding:
698 dec.binding = 0;
699 break;
700
701 case DecorationDescriptorSet:
702 dec.set = 0;
703 break;
704
705 case DecorationInputAttachmentIndex:
706 dec.input_attachment = 0;
707 break;
708
709 case DecorationSpecId:
710 dec.spec_id = 0;
711 break;
712
713 case DecorationHlslSemanticGOOGLE:
714 dec.hlsl_semantic.clear();
715 break;
716
717 case DecorationFPRoundingMode:
718 dec.fp_rounding_mode = FPRoundingModeMax;
719 break;
720
721 case DecorationHlslCounterBufferGOOGLE:
722 {
723 auto &counter = meta[id].hlsl_magic_counter_buffer;
724 if (counter)
725 {
726 meta[counter].hlsl_is_magic_counter_buffer = false;
727 counter = 0;
728 }
729 break;
730 }
731
732 default:
733 break;
734 }
735}
736
737bool ParsedIR::has_member_decoration(TypeID id, uint32_t index, Decoration decoration) const
738{
739 return get_member_decoration_bitset(id, index).get(bit: decoration);
740}
741
742uint32_t ParsedIR::get_member_decoration(TypeID id, uint32_t index, Decoration decoration) const
743{
744 auto *m = find_meta(id);
745 if (!m)
746 return 0;
747
748 if (index >= m->members.size())
749 return 0;
750
751 auto &dec = m->members[index];
752 if (!dec.decoration_flags.get(bit: decoration))
753 return 0;
754
755 switch (decoration)
756 {
757 case DecorationBuiltIn:
758 return dec.builtin_type;
759 case DecorationLocation:
760 return dec.location;
761 case DecorationComponent:
762 return dec.component;
763 case DecorationBinding:
764 return dec.binding;
765 case DecorationOffset:
766 return dec.offset;
767 case DecorationXfbBuffer:
768 return dec.xfb_buffer;
769 case DecorationXfbStride:
770 return dec.xfb_stride;
771 case DecorationStream:
772 return dec.stream;
773 case DecorationSpecId:
774 return dec.spec_id;
775 case DecorationIndex:
776 return dec.index;
777 default:
778 return 1;
779 }
780}
781
782const Bitset &ParsedIR::get_decoration_bitset(ID id) const
783{
784 auto *m = find_meta(id);
785 if (m)
786 {
787 auto &dec = m->decoration;
788 return dec.decoration_flags;
789 }
790 else
791 return cleared_bitset;
792}
793
794void ParsedIR::set_member_decoration_string(TypeID id, uint32_t index, Decoration decoration, const string &argument)
795{
796 auto &m = meta[id];
797 m.members.resize(new_size: max(a: m.members.size(), b: size_t(index) + 1));
798 auto &dec = meta[id].members[index];
799 dec.decoration_flags.set(decoration);
800
801 switch (decoration)
802 {
803 case DecorationHlslSemanticGOOGLE:
804 dec.hlsl_semantic = argument;
805 break;
806
807 default:
808 break;
809 }
810}
811
812const string &ParsedIR::get_member_decoration_string(TypeID id, uint32_t index, Decoration decoration) const
813{
814 auto *m = find_meta(id);
815 if (m)
816 {
817 if (!has_member_decoration(id, index, decoration))
818 return empty_string;
819
820 auto &dec = m->members[index];
821
822 switch (decoration)
823 {
824 case DecorationHlslSemanticGOOGLE:
825 return dec.hlsl_semantic;
826
827 default:
828 return empty_string;
829 }
830 }
831 else
832 return empty_string;
833}
834
835void ParsedIR::unset_member_decoration(TypeID id, uint32_t index, Decoration decoration)
836{
837 auto &m = meta[id];
838 if (index >= m.members.size())
839 return;
840
841 auto &dec = m.members[index];
842
843 dec.decoration_flags.clear(bit: decoration);
844 switch (decoration)
845 {
846 case DecorationBuiltIn:
847 dec.builtin = false;
848 break;
849
850 case DecorationLocation:
851 dec.location = 0;
852 break;
853
854 case DecorationComponent:
855 dec.component = 0;
856 break;
857
858 case DecorationOffset:
859 dec.offset = 0;
860 break;
861
862 case DecorationXfbBuffer:
863 dec.xfb_buffer = 0;
864 break;
865
866 case DecorationXfbStride:
867 dec.xfb_stride = 0;
868 break;
869
870 case DecorationStream:
871 dec.stream = 0;
872 break;
873
874 case DecorationSpecId:
875 dec.spec_id = 0;
876 break;
877
878 case DecorationHlslSemanticGOOGLE:
879 dec.hlsl_semantic.clear();
880 break;
881
882 default:
883 break;
884 }
885}
886
887uint32_t ParsedIR::increase_bound_by(uint32_t incr_amount)
888{
889 auto curr_bound = ids.size();
890 auto new_bound = curr_bound + incr_amount;
891
892 ids.reserve(count: ids.size() + incr_amount);
893 for (uint32_t i = 0; i < incr_amount; i++)
894 ids.emplace_back(ts: pool_group.get());
895
896 block_meta.resize(new_size: new_bound);
897 return uint32_t(curr_bound);
898}
899
900void ParsedIR::remove_typed_id(Types type, ID id)
901{
902 auto &type_ids = ids_for_type[type];
903 type_ids.erase(start_erase: remove(first: begin(cont&: type_ids), last: end(cont&: type_ids), value: id), end_erase: end(cont&: type_ids));
904}
905
906void ParsedIR::reset_all_of_type(Types type)
907{
908 for (auto &id : ids_for_type[type])
909 if (ids[id].get_type() == type)
910 ids[id].reset();
911
912 ids_for_type[type].clear();
913}
914
915void ParsedIR::add_typed_id(Types type, ID id)
916{
917 if (loop_iteration_depth_hard != 0)
918 SPIRV_CROSS_THROW("Cannot add typed ID while looping over it.");
919
920 if (loop_iteration_depth_soft != 0)
921 {
922 if (!ids[id].empty())
923 SPIRV_CROSS_THROW("Cannot override IDs when loop is soft locked.");
924 return;
925 }
926
927 if (ids[id].empty() || ids[id].get_type() != type)
928 {
929 switch (type)
930 {
931 case TypeConstant:
932 ids_for_constant_or_variable.push_back(t: id);
933 ids_for_constant_or_type.push_back(t: id);
934 break;
935
936 case TypeVariable:
937 ids_for_constant_or_variable.push_back(t: id);
938 break;
939
940 case TypeType:
941 case TypeConstantOp:
942 ids_for_constant_or_type.push_back(t: id);
943 break;
944
945 default:
946 break;
947 }
948 }
949
950 if (ids[id].empty())
951 {
952 ids_for_type[type].push_back(t: id);
953 }
954 else if (ids[id].get_type() != type)
955 {
956 remove_typed_id(type: ids[id].get_type(), id);
957 ids_for_type[type].push_back(t: id);
958 }
959}
960
961const Meta *ParsedIR::find_meta(ID id) const
962{
963 auto itr = meta.find(x: id);
964 if (itr != end(cont: meta))
965 return &itr->second;
966 else
967 return nullptr;
968}
969
970Meta *ParsedIR::find_meta(ID id)
971{
972 auto itr = meta.find(x: id);
973 if (itr != end(cont&: meta))
974 return &itr->second;
975 else
976 return nullptr;
977}
978
979ParsedIR::LoopLock ParsedIR::create_loop_hard_lock() const
980{
981 return ParsedIR::LoopLock(&loop_iteration_depth_hard);
982}
983
984ParsedIR::LoopLock ParsedIR::create_loop_soft_lock() const
985{
986 return ParsedIR::LoopLock(&loop_iteration_depth_soft);
987}
988
989ParsedIR::LoopLock::~LoopLock()
990{
991 if (lock)
992 (*lock)--;
993}
994
995ParsedIR::LoopLock::LoopLock(uint32_t *lock_)
996 : lock(lock_)
997{
998 if (lock)
999 (*lock)++;
1000}
1001
1002ParsedIR::LoopLock::LoopLock(LoopLock &&other) SPIRV_CROSS_NOEXCEPT
1003{
1004 *this = std::move(other);
1005}
1006
1007ParsedIR::LoopLock &ParsedIR::LoopLock::operator=(LoopLock &&other) SPIRV_CROSS_NOEXCEPT
1008{
1009 if (lock)
1010 (*lock)--;
1011 lock = other.lock;
1012 other.lock = nullptr;
1013 return *this;
1014}
1015
1016void ParsedIR::make_constant_null(uint32_t id, uint32_t type, bool add_to_typed_id_set)
1017{
1018 auto &constant_type = get<SPIRType>(id: type);
1019
1020 if (constant_type.pointer)
1021 {
1022 if (add_to_typed_id_set)
1023 add_typed_id(type: TypeConstant, id);
1024 auto &constant = variant_set<SPIRConstant>(var&: ids[id], args&: type);
1025 constant.self = id;
1026 constant.make_null(constant_type_: constant_type);
1027 }
1028 else if (!constant_type.array.empty())
1029 {
1030 assert(constant_type.parent_type);
1031 uint32_t parent_id = increase_bound_by(incr_amount: 1);
1032 make_constant_null(id: parent_id, type: constant_type.parent_type, add_to_typed_id_set);
1033
1034 if (!constant_type.array_size_literal.back())
1035 SPIRV_CROSS_THROW("Array size of OpConstantNull must be a literal.");
1036
1037 SmallVector<uint32_t> elements(constant_type.array.back());
1038 for (uint32_t i = 0; i < constant_type.array.back(); i++)
1039 elements[i] = parent_id;
1040
1041 if (add_to_typed_id_set)
1042 add_typed_id(type: TypeConstant, id);
1043 variant_set<SPIRConstant>(var&: ids[id], args&: type, args: elements.data(), args: uint32_t(elements.size()), args: false).self = id;
1044 }
1045 else if (!constant_type.member_types.empty())
1046 {
1047 uint32_t member_ids = increase_bound_by(incr_amount: uint32_t(constant_type.member_types.size()));
1048 SmallVector<uint32_t> elements(constant_type.member_types.size());
1049 for (uint32_t i = 0; i < constant_type.member_types.size(); i++)
1050 {
1051 make_constant_null(id: member_ids + i, type: constant_type.member_types[i], add_to_typed_id_set);
1052 elements[i] = member_ids + i;
1053 }
1054
1055 if (add_to_typed_id_set)
1056 add_typed_id(type: TypeConstant, id);
1057 variant_set<SPIRConstant>(var&: ids[id], args&: type, args: elements.data(), args: uint32_t(elements.size()), args: false).self = id;
1058 }
1059 else
1060 {
1061 if (add_to_typed_id_set)
1062 add_typed_id(type: TypeConstant, id);
1063 auto &constant = variant_set<SPIRConstant>(var&: ids[id], args&: type);
1064 constant.self = id;
1065 constant.make_null(constant_type_: constant_type);
1066 }
1067}
1068
1069} // namespace SPIRV_CROSS_NAMESPACE
1070

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