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 spv; |
30 | |
31 | namespace SPIRV_CROSS_NAMESPACE |
32 | { |
33 | ParsedIR::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. |
55 | ParsedIR::ParsedIR(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT |
56 | { |
57 | *this = std::move(other); |
58 | } |
59 | |
60 | ParsedIR &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 | |
91 | ParsedIR::ParsedIR(const ParsedIR &other) |
92 | : ParsedIR() |
93 | { |
94 | *this = other; |
95 | } |
96 | |
97 | ParsedIR &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 | |
136 | void 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. |
146 | static bool is_alpha(char c) |
147 | { |
148 | return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); |
149 | } |
150 | |
151 | static bool is_numeric(char c) |
152 | { |
153 | return c >= '0' && c <= '9'; |
154 | } |
155 | |
156 | static bool is_alphanumeric(char c) |
157 | { |
158 | return is_alpha(c) || is_numeric(c); |
159 | } |
160 | |
161 | static 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 | |
187 | static 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 | |
196 | static 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 | |
236 | bool 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 | |
241 | uint32_t ParsedIR::get_spirv_version() const |
242 | { |
243 | return spirv[1]; |
244 | } |
245 | |
246 | static 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 | |
254 | void 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 | |
279 | static 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 | |
299 | const 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 | |
308 | const 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 | |
321 | void 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 | |
329 | void 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 | |
341 | void 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 | |
349 | void 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 | |
358 | void 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 | |
374 | void 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 | |
452 | void 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). |
513 | void 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 | |
545 | Bitset 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 | |
556 | Bitset 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 | |
577 | const 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 | |
590 | bool ParsedIR::has_decoration(ID id, Decoration decoration) const |
591 | { |
592 | return get_decoration_bitset(id).get(bit: decoration); |
593 | } |
594 | |
595 | uint32_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 | |
642 | const 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 | |
663 | void 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 | |
737 | bool 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 | |
742 | uint32_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 | |
782 | const 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 | |
794 | void 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 | |
812 | const 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 | |
835 | void 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 | |
887 | uint32_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 | |
900 | void 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 | |
906 | void 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 | |
915 | void 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 | |
961 | const 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 | |
970 | Meta *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 | |
979 | ParsedIR::LoopLock ParsedIR::create_loop_hard_lock() const |
980 | { |
981 | return ParsedIR::LoopLock(&loop_iteration_depth_hard); |
982 | } |
983 | |
984 | ParsedIR::LoopLock ParsedIR::create_loop_soft_lock() const |
985 | { |
986 | return ParsedIR::LoopLock(&loop_iteration_depth_soft); |
987 | } |
988 | |
989 | ParsedIR::LoopLock::~LoopLock() |
990 | { |
991 | if (lock) |
992 | (*lock)--; |
993 | } |
994 | |
995 | ParsedIR::LoopLock::LoopLock(uint32_t *lock_) |
996 | : lock(lock_) |
997 | { |
998 | if (lock) |
999 | (*lock)++; |
1000 | } |
1001 | |
1002 | ParsedIR::LoopLock::LoopLock(LoopLock &&other) SPIRV_CROSS_NOEXCEPT |
1003 | { |
1004 | *this = std::move(other); |
1005 | } |
1006 | |
1007 | ParsedIR::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 | |
1016 | void 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 | |