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_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 | |
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_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 | |
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 | // 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 | |
345 | void 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 | |
353 | void 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 | |
362 | void 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 | |
382 | void 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 | |
460 | void 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). |
521 | void 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 | |
553 | Bitset 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 | |
564 | Bitset 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 | |
585 | const 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 | |
598 | bool ParsedIR::has_decoration(ID id, Decoration decoration) const |
599 | { |
600 | return get_decoration_bitset(id).get(bit: decoration); |
601 | } |
602 | |
603 | uint32_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 | |
650 | const 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 | |
674 | void 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 | |
748 | bool 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 | |
753 | uint32_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 | |
793 | const 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 | |
805 | void 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 | |
823 | const 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 | |
846 | void 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 | |
898 | uint32_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 | |
911 | void 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 | |
917 | void 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 | |
926 | void 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 | |
973 | const 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 | |
982 | Meta *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 | |
991 | ParsedIR::LoopLock ParsedIR::create_loop_hard_lock() const |
992 | { |
993 | return ParsedIR::LoopLock(&loop_iteration_depth_hard); |
994 | } |
995 | |
996 | ParsedIR::LoopLock ParsedIR::create_loop_soft_lock() const |
997 | { |
998 | return ParsedIR::LoopLock(&loop_iteration_depth_soft); |
999 | } |
1000 | |
1001 | ParsedIR::LoopLock::~LoopLock() |
1002 | { |
1003 | if (lock) |
1004 | (*lock)--; |
1005 | } |
1006 | |
1007 | ParsedIR::LoopLock::LoopLock(uint32_t *lock_) |
1008 | : lock(lock_) |
1009 | { |
1010 | if (lock) |
1011 | (*lock)++; |
1012 | } |
1013 | |
1014 | ParsedIR::LoopLock::LoopLock(LoopLock &&other) SPIRV_CROSS_NOEXCEPT |
1015 | { |
1016 | *this = std::move(other); |
1017 | } |
1018 | |
1019 | ParsedIR::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 | |
1028 | void 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 | |