diff --git a/3rdparty/spirv-cross/spirv_cross.cpp b/3rdparty/spirv-cross/spirv_cross.cpp index 519350611..07fdb946f 100644 --- a/3rdparty/spirv-cross/spirv_cross.cpp +++ b/3rdparty/spirv-cross/spirv_cross.cpp @@ -181,6 +181,30 @@ bool Compiler::block_is_pure(const SPIRBlock &block) // This is a global side effect of the function. return false; + case OpExtInst: + { + uint32_t extension_set = ops[2]; + if (get(extension_set).ext == SPIRExtension::GLSL) + { + auto op_450 = static_cast(ops[3]); + switch (op_450) + { + case GLSLstd450Modf: + case GLSLstd450Frexp: + { + auto &type = expression_type(ops[5]); + if (type.storage != StorageClassFunction) + return false; + break; + } + + default: + break; + } + } + break; + } + default: break; } @@ -716,6 +740,15 @@ bool Compiler::InterfaceVariableAccessHandler::handle(Op opcode, const uint32_t break; } + case GLSLstd450Modf: + case GLSLstd450Fract: + { + auto *var = compiler.maybe_get(args[5]); + if (var && storage_class_is_interface(var->storage)) + variables.insert(args[5]); + break; + } + default: break; } @@ -1729,10 +1762,22 @@ size_t Compiler::get_declared_struct_size(const SPIRType &type) const if (type.member_types.empty()) SPIRV_CROSS_THROW("Declared struct in block cannot be empty."); - uint32_t last = uint32_t(type.member_types.size() - 1); - size_t offset = type_struct_member_offset(type, last); - size_t size = get_declared_struct_member_size(type, last); - return offset + size; + // Offsets can be declared out of order, so we need to deduce the actual size + // based on last member instead. + uint32_t member_index = 0; + size_t highest_offset = 0; + for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++) + { + size_t offset = type_struct_member_offset(type, i); + if (offset > highest_offset) + { + highest_offset = offset; + member_index = i; + } + } + + size_t size = get_declared_struct_member_size(type, member_index); + return highest_offset + size; } size_t Compiler::get_declared_struct_size_runtime_array(const SPIRType &type, size_t array_size) const @@ -3278,6 +3323,33 @@ bool Compiler::AnalyzeVariableScopeAccessHandler::handle(spv::Op op, const uint3 for (uint32_t i = 4; i < length; i++) notify_variable_access(args[i], current_block->self); notify_variable_access(args[1], current_block->self); + + uint32_t extension_set = args[2]; + if (compiler.get(extension_set).ext == SPIRExtension::GLSL) + { + auto op_450 = static_cast(args[3]); + switch (op_450) + { + case GLSLstd450Modf: + case GLSLstd450Frexp: + { + uint32_t ptr = args[5]; + auto *var = compiler.maybe_get_backing_variable(ptr); + if (var) + { + accessed_variables_to_block[var->self].insert(current_block->self); + if (var->self == ptr) + complete_write_variables_to_block[var->self].insert(current_block->self); + else + partial_write_variables_to_block[var->self].insert(current_block->self); + } + break; + } + + default: + break; + } + } break; } @@ -4677,31 +4749,181 @@ Compiler::PhysicalStorageBufferPointerHandler::PhysicalStorageBufferPointerHandl { } -bool Compiler::PhysicalStorageBufferPointerHandler::handle(Op op, const uint32_t *args, uint32_t) +Compiler::PhysicalBlockMeta *Compiler::PhysicalStorageBufferPointerHandler::find_block_meta(uint32_t id) const { - if (op == OpConvertUToPtr || op == OpBitcast) + auto chain_itr = access_chain_to_physical_block.find(id); + if (chain_itr != access_chain_to_physical_block.end()) + return chain_itr->second; + else + return nullptr; +} + +void Compiler::PhysicalStorageBufferPointerHandler::mark_aligned_access(uint32_t id, const uint32_t *args, uint32_t length) +{ + uint32_t mask = *args; + args++; + length--; + if (length && (mask & MemoryAccessVolatileMask) != 0) { - auto &type = compiler.get(args[0]); - if (type.storage == StorageClassPhysicalStorageBufferEXT && type.pointer && type.pointer_depth == 1) + args++; + length--; + } + + if (length && (mask & MemoryAccessAlignedMask) != 0) + { + uint32_t alignment = *args; + auto *meta = find_block_meta(id); + + // This makes the assumption that the application does not rely on insane edge cases like: + // Bind buffer with ADDR = 8, use block offset of 8 bytes, load/store with 16 byte alignment. + // If we emit the buffer with alignment = 16 here, the first element at offset = 0 should + // actually have alignment of 8 bytes, but this is too theoretical and awkward to support. + // We could potentially keep track of any offset in the access chain, but it's + // practically impossible for high level compilers to emit code like that, + // so deducing overall alignment requirement based on maximum observed Alignment value is probably fine. + if (meta && alignment > meta->alignment) + meta->alignment = alignment; + } +} + +bool Compiler::PhysicalStorageBufferPointerHandler::type_is_bda_block_entry(uint32_t type_id) const +{ + auto &type = compiler.get(type_id); + return type.storage == StorageClassPhysicalStorageBufferEXT && type.pointer && + type.pointer_depth == 1 && !compiler.type_is_array_of_pointers(type); +} + +uint32_t Compiler::PhysicalStorageBufferPointerHandler::get_minimum_scalar_alignment(const SPIRType &type) const +{ + if (type.storage == spv::StorageClassPhysicalStorageBufferEXT) + return 8; + else if (type.basetype == SPIRType::Struct) + { + uint32_t alignment = 0; + for (auto &member_type : type.member_types) { - // If we need to cast to a pointer type which is not a block, we might need to synthesize ourselves - // a block type which wraps this POD type. - if (type.basetype != SPIRType::Struct) - types.insert(args[0]); + uint32_t member_align = get_minimum_scalar_alignment(compiler.get(member_type)); + if (member_align > alignment) + alignment = member_align; } + return alignment; + } + else + return type.width / 8; +} + +void Compiler::PhysicalStorageBufferPointerHandler::setup_meta_chain(uint32_t type_id, uint32_t var_id) +{ + if (type_is_bda_block_entry(type_id)) + { + auto &meta = physical_block_type_meta[type_id]; + access_chain_to_physical_block[var_id] = &meta; + + auto &type = compiler.get(type_id); + if (type.basetype != SPIRType::Struct) + non_block_types.insert(type_id); + + if (meta.alignment == 0) + meta.alignment = get_minimum_scalar_alignment(compiler.get_pointee_type(type)); + } +} + +bool Compiler::PhysicalStorageBufferPointerHandler::handle(Op op, const uint32_t *args, uint32_t length) +{ + // When a BDA pointer comes to life, we need to keep a mapping of SSA ID -> type ID for the pointer type. + // For every load and store, we'll need to be able to look up the type ID being accessed and mark any alignment + // requirements. + switch (op) + { + case OpConvertUToPtr: + case OpBitcast: + case OpCompositeExtract: + // Extract can begin a new chain if we had a struct or array of pointers as input. + // We don't begin chains before we have a pure scalar pointer. + setup_meta_chain(args[0], args[1]); + break; + + case OpAccessChain: + case OpInBoundsAccessChain: + case OpPtrAccessChain: + case OpCopyObject: + { + auto itr = access_chain_to_physical_block.find(args[2]); + if (itr != access_chain_to_physical_block.end()) + access_chain_to_physical_block[args[1]] = itr->second; + break; + } + + case OpLoad: + { + setup_meta_chain(args[0], args[1]); + if (length >= 4) + mark_aligned_access(args[2], args + 3, length - 3); + break; + } + + case OpStore: + { + if (length >= 3) + mark_aligned_access(args[0], args + 2, length - 2); + break; + } + + default: + break; } return true; } +uint32_t Compiler::PhysicalStorageBufferPointerHandler::get_base_non_block_type_id(uint32_t type_id) const +{ + auto *type = &compiler.get(type_id); + while (type->pointer && + type->storage == StorageClassPhysicalStorageBufferEXT && + !type_is_bda_block_entry(type_id)) + { + type_id = type->parent_type; + type = &compiler.get(type_id); + } + + assert(type_is_bda_block_entry(type_id)); + return type_id; +} + +void Compiler::PhysicalStorageBufferPointerHandler::analyze_non_block_types_from_block(const SPIRType &type) +{ + for (auto &member : type.member_types) + { + auto &subtype = compiler.get(member); + if (subtype.basetype != SPIRType::Struct && subtype.pointer && + subtype.storage == spv::StorageClassPhysicalStorageBufferEXT) + { + non_block_types.insert(get_base_non_block_type_id(member)); + } + else if (subtype.basetype == SPIRType::Struct && !subtype.pointer) + analyze_non_block_types_from_block(subtype); + } +} + void Compiler::analyze_non_block_pointer_types() { PhysicalStorageBufferPointerHandler handler(*this); traverse_all_reachable_opcodes(get(ir.default_entry_point), handler); - physical_storage_non_block_pointer_types.reserve(handler.types.size()); - for (auto type : handler.types) + + // Analyze any block declaration we have to make. It might contain + // physical pointers to POD types which we never used, and thus never added to the list. + // We'll need to add those pointer types to the set of types we declare. + ir.for_each_typed_id([&](uint32_t, SPIRType &type) { + if (has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock)) + handler.analyze_non_block_types_from_block(type); + }); + + physical_storage_non_block_pointer_types.reserve(handler.non_block_types.size()); + for (auto type : handler.non_block_types) physical_storage_non_block_pointer_types.push_back(type); sort(begin(physical_storage_non_block_pointer_types), end(physical_storage_non_block_pointer_types)); + physical_storage_type_to_alignment = move(handler.physical_block_type_meta); } bool Compiler::InterlockedResourceAccessPrepassHandler::handle(Op op, const uint32_t *, uint32_t) diff --git a/3rdparty/spirv-cross/spirv_cross.hpp b/3rdparty/spirv-cross/spirv_cross.hpp index 27308601a..d8967963f 100644 --- a/3rdparty/spirv-cross/spirv_cross.hpp +++ b/3rdparty/spirv-cross/spirv_cross.hpp @@ -1010,15 +1010,32 @@ protected: uint32_t write_count = 0; }; + struct PhysicalBlockMeta + { + uint32_t alignment = 0; + }; + struct PhysicalStorageBufferPointerHandler : OpcodeHandler { explicit PhysicalStorageBufferPointerHandler(Compiler &compiler_); bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; Compiler &compiler; - std::unordered_set types; + + std::unordered_set non_block_types; + std::unordered_map physical_block_type_meta; + std::unordered_map access_chain_to_physical_block; + + void mark_aligned_access(uint32_t id, const uint32_t *args, uint32_t length); + PhysicalBlockMeta *find_block_meta(uint32_t id) const; + bool type_is_bda_block_entry(uint32_t type_id) const; + void setup_meta_chain(uint32_t type_id, uint32_t var_id); + uint32_t get_minimum_scalar_alignment(const SPIRType &type) const; + void analyze_non_block_types_from_block(const SPIRType &type); + uint32_t get_base_non_block_type_id(uint32_t type_id) const; }; void analyze_non_block_pointer_types(); SmallVector physical_storage_non_block_pointer_types; + std::unordered_map physical_storage_type_to_alignment; void analyze_variable_scope(SPIRFunction &function, AnalyzeVariableScopeAccessHandler &handler); void find_function_local_luts(SPIRFunction &function, const AnalyzeVariableScopeAccessHandler &handler, diff --git a/3rdparty/spirv-cross/spirv_cross_containers.hpp b/3rdparty/spirv-cross/spirv_cross_containers.hpp index a027250bd..506b069c7 100644 --- a/3rdparty/spirv-cross/spirv_cross_containers.hpp +++ b/3rdparty/spirv-cross/spirv_cross_containers.hpp @@ -327,8 +327,8 @@ public: void reserve(size_t count) SPIRV_CROSS_NOEXCEPT { - if ((count > std::numeric_limits::max() / sizeof(T)) || - (count > std::numeric_limits::max() / 2)) + if ((count > (std::numeric_limits::max)() / sizeof(T)) || + (count > (std::numeric_limits::max)() / 2)) { // Only way this should ever happen is with garbage input, terminate. std::terminate(); diff --git a/3rdparty/spirv-cross/spirv_glsl.cpp b/3rdparty/spirv-cross/spirv_glsl.cpp index 61acda6a0..9578502c1 100644 --- a/3rdparty/spirv-cross/spirv_glsl.cpp +++ b/3rdparty/spirv-cross/spirv_glsl.cpp @@ -2171,8 +2171,9 @@ void CompilerGLSL::emit_buffer_block_legacy(const SPIRVariable &var) statement(""); } -void CompilerGLSL::emit_buffer_reference_block(SPIRType &type, bool forward_declaration) +void CompilerGLSL::emit_buffer_reference_block(uint32_t type_id, bool forward_declaration) { + auto &type = get(type_id); string buffer_name; if (forward_declaration) @@ -2215,8 +2216,19 @@ void CompilerGLSL::emit_buffer_reference_block(SPIRType &type, bool forward_decl if (!forward_declaration) { + auto itr = physical_storage_type_to_alignment.find(type_id); + uint32_t alignment = 0; + if (itr != physical_storage_type_to_alignment.end()) + alignment = itr->second.alignment; + if (type.basetype == SPIRType::Struct) { + SmallVector attributes; + attributes.push_back("buffer_reference"); + if (alignment) + attributes.push_back(join("buffer_reference_align = ", alignment)); + attributes.push_back(buffer_to_packing_standard(type, true)); + auto flags = ir.get_buffer_block_type_flags(type); string decorations; if (flags.get(DecorationRestrict)) @@ -2227,9 +2239,11 @@ void CompilerGLSL::emit_buffer_reference_block(SPIRType &type, bool forward_decl decorations += " writeonly"; if (flags.get(DecorationNonWritable)) decorations += " readonly"; - statement("layout(buffer_reference, ", buffer_to_packing_standard(type, true), - ")", decorations, " buffer ", buffer_name); + + statement("layout(", merge(attributes), ")", decorations, " buffer ", buffer_name); } + else if (alignment) + statement("layout(buffer_reference, buffer_reference_align = ", alignment, ") buffer ", buffer_name); else statement("layout(buffer_reference) buffer ", buffer_name); @@ -3447,28 +3461,28 @@ void CompilerGLSL::emit_resources() { for (auto type : physical_storage_non_block_pointer_types) { - emit_buffer_reference_block(get(type), false); + emit_buffer_reference_block(type, false); } // Output buffer reference blocks. // Do this in two stages, one with forward declaration, // and one without. Buffer reference blocks can reference themselves // to support things like linked lists. - ir.for_each_typed_id([&](uint32_t, SPIRType &type) { - bool has_block_flags = has_decoration(type.self, DecorationBlock); - if (has_block_flags && type.pointer && type.pointer_depth == 1 && !type_is_array_of_pointers(type) && + ir.for_each_typed_id([&](uint32_t self, SPIRType &type) { + if (type.basetype == SPIRType::Struct && type.pointer && + type.pointer_depth == 1 && !type_is_array_of_pointers(type) && type.storage == StorageClassPhysicalStorageBufferEXT) { - emit_buffer_reference_block(type, true); + emit_buffer_reference_block(self, true); } }); - ir.for_each_typed_id([&](uint32_t, SPIRType &type) { - bool has_block_flags = has_decoration(type.self, DecorationBlock); - if (has_block_flags && type.pointer && type.pointer_depth == 1 && !type_is_array_of_pointers(type) && + ir.for_each_typed_id([&](uint32_t self, SPIRType &type) { + if (type.basetype == SPIRType::Struct && + type.pointer && type.pointer_depth == 1 && !type_is_array_of_pointers(type) && type.storage == StorageClassPhysicalStorageBufferEXT) { - emit_buffer_reference_block(type, false); + emit_buffer_reference_block(self, false); } }); } diff --git a/3rdparty/spirv-cross/spirv_glsl.hpp b/3rdparty/spirv-cross/spirv_glsl.hpp index 52839d69c..523624429 100644 --- a/3rdparty/spirv-cross/spirv_glsl.hpp +++ b/3rdparty/spirv-cross/spirv_glsl.hpp @@ -592,7 +592,7 @@ protected: void emit_resources(); void emit_extension_workarounds(spv::ExecutionModel model); void emit_buffer_block_native(const SPIRVariable &var); - void emit_buffer_reference_block(SPIRType &type, bool forward_declaration); + void emit_buffer_reference_block(uint32_t type_id, bool forward_declaration); void emit_buffer_block_legacy(const SPIRVariable &var); void emit_buffer_block_flattened(const SPIRVariable &type); void fixup_implicit_builtin_block_names(); diff --git a/3rdparty/spirv-cross/spirv_msl.cpp b/3rdparty/spirv-cross/spirv_msl.cpp index a7ec6d97f..3f53ebd58 100644 --- a/3rdparty/spirv-cross/spirv_msl.cpp +++ b/3rdparty/spirv-cross/spirv_msl.cpp @@ -1710,6 +1710,16 @@ void CompilerMSL::extract_global_variables_from_function(uint32_t func_id, std:: added_arg_ids.insert(stage_in_var_id); break; } + + case GLSLstd450Modf: + case GLSLstd450Frexp: + { + uint32_t base_id = ops[5]; + if (global_var_ids.find(base_id) != global_var_ids.end()) + added_arg_ids.insert(base_id); + break; + } + default: break; } @@ -2251,7 +2261,7 @@ void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, co set_member_decoration(ib_type.self, ib_mbr_idx, DecorationComponent, comp); mark_location_as_used_by_shader(locn, get(type_id), storage); } - else if (is_builtin && is_tessellation_shader() && inputs_by_builtin.count(builtin)) + else if (is_builtin && is_tessellation_shader() && storage == StorageClassInput && inputs_by_builtin.count(builtin)) { uint32_t locn = inputs_by_builtin[builtin].location; set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); @@ -2416,7 +2426,7 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage set_member_decoration(ib_type.self, ib_mbr_idx, DecorationComponent, comp); mark_location_as_used_by_shader(locn, *usable_type, storage); } - else if (is_builtin && is_tessellation_shader() && inputs_by_builtin.count(builtin)) + else if (is_builtin && is_tessellation_shader() && storage == StorageClassInput && inputs_by_builtin.count(builtin)) { uint32_t locn = inputs_by_builtin[builtin].location + i; set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); @@ -2589,7 +2599,7 @@ void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); mark_location_as_used_by_shader(locn, *usable_type, storage); } - else if (is_builtin && is_tessellation_shader() && inputs_by_builtin.count(builtin)) + else if (is_builtin && is_tessellation_shader() && storage == StorageClassInput && inputs_by_builtin.count(builtin)) { uint32_t locn = inputs_by_builtin[builtin].location + i; set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); @@ -2780,7 +2790,7 @@ void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass stor set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); mark_location_as_used_by_shader(locn, get(mbr_type_id), storage); } - else if (is_builtin && is_tessellation_shader() && inputs_by_builtin.count(builtin)) + else if (is_builtin && is_tessellation_shader() && storage == StorageClassInput && inputs_by_builtin.count(builtin)) { uint32_t locn = 0; auto builtin_itr = inputs_by_builtin.find(builtin); @@ -4171,11 +4181,7 @@ void CompilerMSL::ensure_member_packing_rules_msl(SPIRType &ib_type, uint32_t in set_decoration(type_id, DecorationArrayStride, array_stride); // Remove packed_ for vectors of size 1, 2 and 4. - if (has_extended_decoration(ib_type.self, SPIRVCrossDecorationPhysicalTypePacked)) - SPIRV_CROSS_THROW("Unable to remove packed decoration as entire struct must be fully packed. Do not mix " - "scalar and std140 layout rules."); - else - unset_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypePacked); + unset_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypePacked); } else if (is_matrix(mbr_type)) { @@ -4202,11 +4208,7 @@ void CompilerMSL::ensure_member_packing_rules_msl(SPIRType &ib_type, uint32_t in set_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypeID, type_id); // Remove packed_ for vectors of size 1, 2 and 4. - if (has_extended_decoration(ib_type.self, SPIRVCrossDecorationPhysicalTypePacked)) - SPIRV_CROSS_THROW("Unable to remove packed decoration as entire struct must be fully packed. Do not mix " - "scalar and std140 layout rules."); - else - unset_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypePacked); + unset_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypePacked); } else SPIRV_CROSS_THROW("Found a buffer packing case which we cannot represent in MSL."); @@ -9171,8 +9173,16 @@ void CompilerMSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop, case GLSLstd450Frexp: { // Special case. If the variable is a scalar access chain, we cannot use it directly. We have to emit a temporary. + // Another special case is if the variable is in a storage class which is not thread. auto *ptr = maybe_get(args[1]); - if (ptr && ptr->access_chain && is_scalar(expression_type(args[1]))) + auto &type = expression_type(args[1]); + + bool is_thread_storage = storage_class_array_is_thread(type.storage); + if (type.storage == StorageClassOutput && capture_output_to_buffer) + is_thread_storage = false; + + if (!is_thread_storage || + (ptr && ptr->access_chain && is_scalar(expression_type(args[1])))) { register_call_out_argument(args[1]); forced_temporaries.insert(id); @@ -9183,7 +9193,7 @@ void CompilerMSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop, if (!tmp_id) tmp_id = ir.increase_bound_by(1); - uint32_t tmp_type_id = get_pointee_type_id(ptr->expression_type); + uint32_t tmp_type_id = get_pointee_type_id(expression_type_id(args[1])); emit_uninitialized_temporary_expression(tmp_type_id, tmp_id); emit_binary_func_op(result_type, id, args[0], tmp_id, eop == GLSLstd450Modf ? "modf" : "frexp"); statement(to_expression(args[1]), " = ", to_expression(tmp_id), ";"); diff --git a/3rdparty/spirv-cross/spirv_parser.cpp b/3rdparty/spirv-cross/spirv_parser.cpp index d50d2e842..58fcec101 100644 --- a/3rdparty/spirv-cross/spirv_parser.cpp +++ b/3rdparty/spirv-cross/spirv_parser.cpp @@ -961,6 +961,49 @@ void Parser::parse(const Instruction &instruction) current_block->false_block = ops[2]; current_block->terminator = SPIRBlock::Select; + + if (current_block->true_block == current_block->false_block) + { + // Bogus conditional, translate to a direct branch. + // Avoids some ugly edge cases later when analyzing CFGs. + + // There are some super jank cases where the merge block is different from the true/false, + // and later branches can "break" out of the selection construct this way. + // This is complete nonsense, but CTS hits this case. + // In this scenario, we should see the selection construct as more of a Switch with one default case. + // The problem here is that this breaks any attempt to break out of outer switch statements, + // but it's theoretically solvable if this ever comes up using the ladder breaking system ... + + if (current_block->true_block != current_block->next_block && + current_block->merge == SPIRBlock::MergeSelection) + { + uint32_t ids = ir.increase_bound_by(2); + + SPIRType type; + type.basetype = SPIRType::Int; + type.width = 32; + set(ids, type); + auto &c = set(ids + 1, ids); + + current_block->condition = c.self; + current_block->default_block = current_block->true_block; + current_block->terminator = SPIRBlock::MultiSelect; + ir.block_meta[current_block->next_block] &= ~ParsedIR::BLOCK_META_SELECTION_MERGE_BIT; + ir.block_meta[current_block->next_block] |= ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT; + } + else + { + ir.block_meta[current_block->next_block] &= ~ParsedIR::BLOCK_META_SELECTION_MERGE_BIT; + current_block->next_block = current_block->true_block; + current_block->condition = 0; + current_block->true_block = 0; + current_block->false_block = 0; + current_block->merge_block = 0; + current_block->merge = SPIRBlock::MergeNone; + current_block->terminator = SPIRBlock::Direct; + } + } + current_block = nullptr; break; }