| 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 |  |