spirv: Rewrite CFG construction
This commit completely rewrites the way we extract a structured CFG from SPIR-V. The new approach is different in a few ways:
It does a breadth-first search instead of depth-first. This means that we've visited the merge node for a construct before we visit any of the nodes inside the construct. This makes it easier to validate things like loop and switch nesting.
We record more information in the CFG. Earlier commits added a parent pointer to vtn_cf_node but we now record all of the merge and other special blocks for each CFG node. This lets us validate things more precisely.
It makes heavy use of merge blocks for walking the CFG. Previously, we sort of used them as hints for trying to guess the CFG structure but things got dicey whenever a merge was missing. We had some heuristics for how to handle short-circuiting if statements but it was a bunch of special cases.
Now, we make them a fundamental part of walking the CFG. When we encounter a control-flow construct, we add the body components of the construct to the BFS work list and then jump to the merge block if one exists to continue scanning the current CFG nesting level. If no merge block exists, we assume that means that control-flow never re-converges in a normal way and that the only way to get back to normality is with a direct jump such as a loop break or continue. This should make things far more robust when trying to deal with the more creative placement (or lack thereof) of merge instructions.
This is an un-revert of !3820 (merged). It's already been reviewed but caused some regressions in RADV. Plan to re-merge once we get an ACK from the RADV team.