Finds the fall through case construct of |target_block| and records it in |case_fall_through|. Returns SPV_ERROR_INVALID_CFG if the case construct headed by |target_block| branches to multiple case constructs.
| 495 | // |case_fall_through|. Returns SPV_ERROR_INVALID_CFG if the case construct |
| 496 | // headed by |target_block| branches to multiple case constructs. |
| 497 | spv_result_t FindCaseFallThrough( |
| 498 | ValidationState_t& _, BasicBlock* target_block, uint32_t* case_fall_through, |
| 499 | const Construct& switch_construct, |
| 500 | const std::unordered_set<uint32_t>& case_targets) { |
| 501 | const auto* merge = switch_construct.exit_block(); |
| 502 | std::vector<BasicBlock*> stack; |
| 503 | stack.push_back(target_block); |
| 504 | std::unordered_set<const BasicBlock*> visited; |
| 505 | bool target_reachable = target_block->structurally_reachable(); |
| 506 | while (!stack.empty()) { |
| 507 | auto block = stack.back(); |
| 508 | stack.pop_back(); |
| 509 | |
| 510 | if (block == merge) continue; |
| 511 | |
| 512 | if (!visited.insert(block).second) continue; |
| 513 | |
| 514 | if (target_reachable && block->structurally_reachable() && |
| 515 | target_block->structurally_dominates(*block)) { |
| 516 | // Still in the case construct. |
| 517 | for (auto successor : *block->successors()) { |
| 518 | stack.push_back(successor); |
| 519 | } |
| 520 | } else { |
| 521 | // Exiting the case construct to non-merge block. |
| 522 | if (!case_targets.count(block->id())) { |
| 523 | // We have already filtered out the following: |
| 524 | // * The switch's merge |
| 525 | // * Other case targets |
| 526 | // * Blocks in the same case construct |
| 527 | // |
| 528 | // So the only remaining valid branches are the structured exits from |
| 529 | // the overall selection construct of the switch. |
| 530 | if (switch_construct.IsStructuredExit(_, block)) { |
| 531 | continue; |
| 532 | } |
| 533 | |
| 534 | return _.diag(SPV_ERROR_INVALID_CFG, target_block->label()) |
| 535 | << "Case construct that targets " |
| 536 | << _.getIdName(target_block->id()) |
| 537 | << " has invalid branch to block " << _.getIdName(block->id()) |
| 538 | << " (not another case construct, corresponding merge, outer " |
| 539 | "loop merge or outer loop continue)"; |
| 540 | } |
| 541 | |
| 542 | if (*case_fall_through == 0u) { |
| 543 | if (target_block != block) { |
| 544 | *case_fall_through = block->id(); |
| 545 | } |
| 546 | } else if (*case_fall_through != block->id()) { |
| 547 | // Case construct has at most one branch to another case construct. |
| 548 | return _.diag(SPV_ERROR_INVALID_CFG, target_block->label()) |
| 549 | << "Case construct that targets " |
| 550 | << _.getIdName(target_block->id()) |
| 551 | << " has branches to multiple other case construct targets " |
| 552 | << _.getIdName(*case_fall_through) << " and " |
| 553 | << _.getIdName(block->id()); |
| 554 | } |
no test coverage detected