diff options
Diffstat (limited to '3rdparty/bgfx/3rdparty/spirv-tools/source/val/validate_cfg.cpp')
-rw-r--r-- | 3rdparty/bgfx/3rdparty/spirv-tools/source/val/validate_cfg.cpp | 68 |
1 files changed, 66 insertions, 2 deletions
diff --git a/3rdparty/bgfx/3rdparty/spirv-tools/source/val/validate_cfg.cpp b/3rdparty/bgfx/3rdparty/spirv-tools/source/val/validate_cfg.cpp index 8d8839e2fbe..4801fc58a29 100644 --- a/3rdparty/bgfx/3rdparty/spirv-tools/source/val/validate_cfg.cpp +++ b/3rdparty/bgfx/3rdparty/spirv-tools/source/val/validate_cfg.cpp @@ -590,9 +590,68 @@ spv_result_t StructuredSwitchChecks(ValidationState_t& _, Function* function, return SPV_SUCCESS; } +// Validates that all CFG divergences (i.e. conditional branch or switch) are +// structured correctly. Either divergence is preceded by a merge instruction +// or the divergence introduces at most one unseen label. +spv_result_t ValidateStructuredSelections( + ValidationState_t& _, const std::vector<const BasicBlock*>& postorder) { + std::unordered_set<uint32_t> seen; + for (auto iter = postorder.rbegin(); iter != postorder.rend(); ++iter) { + const auto* block = *iter; + const auto* terminator = block->terminator(); + if (!terminator) continue; + const auto index = terminator - &_.ordered_instructions()[0]; + auto* merge = &_.ordered_instructions()[index - 1]; + // Marks merges and continues as seen. + if (merge->opcode() == SpvOpSelectionMerge) { + seen.insert(merge->GetOperandAs<uint32_t>(0)); + } else if (merge->opcode() == SpvOpLoopMerge) { + seen.insert(merge->GetOperandAs<uint32_t>(0)); + seen.insert(merge->GetOperandAs<uint32_t>(1)); + } else { + // Only track the pointer if it is a merge instruction. + merge = nullptr; + } + + // Skip unreachable blocks. + if (!block->reachable()) continue; + + if (terminator->opcode() == SpvOpBranchConditional) { + const auto true_label = terminator->GetOperandAs<uint32_t>(1); + const auto false_label = terminator->GetOperandAs<uint32_t>(2); + // Mark the upcoming blocks as seen now, but only error out if this block + // was missing a merge instruction and both labels hadn't been seen + // previously. + const bool both_unseen = + seen.insert(true_label).second && seen.insert(false_label).second; + if (!merge && both_unseen) { + return _.diag(SPV_ERROR_INVALID_CFG, terminator) + << "Selection must be structured"; + } + } else if (terminator->opcode() == SpvOpSwitch) { + uint32_t count = 0; + // Mark the targets as seen now, but only error out if this block was + // missing a merge instruction and there were multiple unseen labels. + for (uint32_t i = 1; i < terminator->operands().size(); i += 2) { + const auto target = terminator->GetOperandAs<uint32_t>(i); + if (seen.insert(target).second) { + count++; + } + } + if (!merge && count > 1) { + return _.diag(SPV_ERROR_INVALID_CFG, terminator) + << "Selection must be structured"; + } + } + } + + return SPV_SUCCESS; +} + spv_result_t StructuredControlFlowChecks( ValidationState_t& _, Function* function, - const std::vector<std::pair<uint32_t, uint32_t>>& back_edges) { + const std::vector<std::pair<uint32_t, uint32_t>>& back_edges, + const std::vector<const BasicBlock*>& postorder) { /// Check all backedges target only loop headers and have exactly one /// back-edge branching to it @@ -709,6 +768,10 @@ spv_result_t StructuredControlFlowChecks( } } + if (auto error = ValidateStructuredSelections(_, postorder)) { + return error; + } + return SPV_SUCCESS; } @@ -931,7 +994,8 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) { /// Structured control flow checks are only required for shader capabilities if (_.HasCapability(SpvCapabilityShader)) { - if (auto error = StructuredControlFlowChecks(_, &function, back_edges)) + if (auto error = + StructuredControlFlowChecks(_, &function, back_edges, postorder)) return error; } } |