Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/KhronosGroup/SPIRV-Cross.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans-Kristian Arntzen <post@arntzen-software.no>2021-11-07 14:37:23 +0300
committerHans-Kristian Arntzen <post@arntzen-software.no>2021-11-07 15:32:29 +0300
commit2714f5410c50a882c5653485cf17b4f96378cf17 (patch)
treee756c59a0825760780294b86bd0445ec8e9a2f1b /spirv_parser.cpp
parent04293e03fdc6205ba9e0e52c54d7901a4e67ae13 (diff)
CFG: Handle degenerate selection constructs.
Apparently, it's legal to use a selection construct where both paths branch to same location, but a different merge point is used. This breaks many assumptions the variable scope analyzer makes. The only logical way to generate code for this scenario is to treat the selection construct as a trivial switch construct with only a default case.
Diffstat (limited to 'spirv_parser.cpp')
-rw-r--r--spirv_parser.cpp43
1 files changed, 43 insertions, 0 deletions
diff --git a/spirv_parser.cpp b/spirv_parser.cpp
index d50d2e84..58fcec10 100644
--- a/spirv_parser.cpp
+++ b/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<SPIRType>(ids, type);
+ auto &c = set<SPIRConstant>(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;
}