1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2016-2021 Arm Limited 3cb93a386Sopenharmony_ci * SPDX-License-Identifier: Apache-2.0 OR MIT 4cb93a386Sopenharmony_ci * 5cb93a386Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 6cb93a386Sopenharmony_ci * you may not use this file except in compliance with the License. 7cb93a386Sopenharmony_ci * You may obtain a copy of the License at 8cb93a386Sopenharmony_ci * 9cb93a386Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 10cb93a386Sopenharmony_ci * 11cb93a386Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software 12cb93a386Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 13cb93a386Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14cb93a386Sopenharmony_ci * See the License for the specific language governing permissions and 15cb93a386Sopenharmony_ci * limitations under the License. 16cb93a386Sopenharmony_ci */ 17cb93a386Sopenharmony_ci 18cb93a386Sopenharmony_ci/* 19cb93a386Sopenharmony_ci * At your option, you may choose to accept this material under either: 20cb93a386Sopenharmony_ci * 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or 21cb93a386Sopenharmony_ci * 2. The MIT License, found at <http://opensource.org/licenses/MIT>. 22cb93a386Sopenharmony_ci */ 23cb93a386Sopenharmony_ci 24cb93a386Sopenharmony_ci#include "spirv_cfg.hpp" 25cb93a386Sopenharmony_ci#include "spirv_cross.hpp" 26cb93a386Sopenharmony_ci#include <algorithm> 27cb93a386Sopenharmony_ci#include <assert.h> 28cb93a386Sopenharmony_ci 29cb93a386Sopenharmony_ciusing namespace std; 30cb93a386Sopenharmony_ci 31cb93a386Sopenharmony_cinamespace SPIRV_CROSS_NAMESPACE 32cb93a386Sopenharmony_ci{ 33cb93a386Sopenharmony_ciCFG::CFG(Compiler &compiler_, const SPIRFunction &func_) 34cb93a386Sopenharmony_ci : compiler(compiler_) 35cb93a386Sopenharmony_ci , func(func_) 36cb93a386Sopenharmony_ci{ 37cb93a386Sopenharmony_ci build_post_order_visit_order(); 38cb93a386Sopenharmony_ci build_immediate_dominators(); 39cb93a386Sopenharmony_ci} 40cb93a386Sopenharmony_ci 41cb93a386Sopenharmony_ciuint32_t CFG::find_common_dominator(uint32_t a, uint32_t b) const 42cb93a386Sopenharmony_ci{ 43cb93a386Sopenharmony_ci while (a != b) 44cb93a386Sopenharmony_ci { 45cb93a386Sopenharmony_ci if (get_visit_order(a) < get_visit_order(b)) 46cb93a386Sopenharmony_ci a = get_immediate_dominator(a); 47cb93a386Sopenharmony_ci else 48cb93a386Sopenharmony_ci b = get_immediate_dominator(b); 49cb93a386Sopenharmony_ci } 50cb93a386Sopenharmony_ci return a; 51cb93a386Sopenharmony_ci} 52cb93a386Sopenharmony_ci 53cb93a386Sopenharmony_civoid CFG::build_immediate_dominators() 54cb93a386Sopenharmony_ci{ 55cb93a386Sopenharmony_ci // Traverse the post-order in reverse and build up the immediate dominator tree. 56cb93a386Sopenharmony_ci immediate_dominators.clear(); 57cb93a386Sopenharmony_ci immediate_dominators[func.entry_block] = func.entry_block; 58cb93a386Sopenharmony_ci 59cb93a386Sopenharmony_ci for (auto i = post_order.size(); i; i--) 60cb93a386Sopenharmony_ci { 61cb93a386Sopenharmony_ci uint32_t block = post_order[i - 1]; 62cb93a386Sopenharmony_ci auto &pred = preceding_edges[block]; 63cb93a386Sopenharmony_ci if (pred.empty()) // This is for the entry block, but we've already set up the dominators. 64cb93a386Sopenharmony_ci continue; 65cb93a386Sopenharmony_ci 66cb93a386Sopenharmony_ci for (auto &edge : pred) 67cb93a386Sopenharmony_ci { 68cb93a386Sopenharmony_ci if (immediate_dominators[block]) 69cb93a386Sopenharmony_ci { 70cb93a386Sopenharmony_ci assert(immediate_dominators[edge]); 71cb93a386Sopenharmony_ci immediate_dominators[block] = find_common_dominator(immediate_dominators[block], edge); 72cb93a386Sopenharmony_ci } 73cb93a386Sopenharmony_ci else 74cb93a386Sopenharmony_ci immediate_dominators[block] = edge; 75cb93a386Sopenharmony_ci } 76cb93a386Sopenharmony_ci } 77cb93a386Sopenharmony_ci} 78cb93a386Sopenharmony_ci 79cb93a386Sopenharmony_cibool CFG::is_back_edge(uint32_t to) const 80cb93a386Sopenharmony_ci{ 81cb93a386Sopenharmony_ci // We have a back edge if the visit order is set with the temporary magic value 0. 82cb93a386Sopenharmony_ci // Crossing edges will have already been recorded with a visit order. 83cb93a386Sopenharmony_ci auto itr = visit_order.find(to); 84cb93a386Sopenharmony_ci return itr != end(visit_order) && itr->second.get() == 0; 85cb93a386Sopenharmony_ci} 86cb93a386Sopenharmony_ci 87cb93a386Sopenharmony_cibool CFG::has_visited_forward_edge(uint32_t to) const 88cb93a386Sopenharmony_ci{ 89cb93a386Sopenharmony_ci // If > 0, we have visited the edge already, and this is not a back edge branch. 90cb93a386Sopenharmony_ci auto itr = visit_order.find(to); 91cb93a386Sopenharmony_ci return itr != end(visit_order) && itr->second.get() > 0; 92cb93a386Sopenharmony_ci} 93cb93a386Sopenharmony_ci 94cb93a386Sopenharmony_cibool CFG::post_order_visit(uint32_t block_id) 95cb93a386Sopenharmony_ci{ 96cb93a386Sopenharmony_ci // If we have already branched to this block (back edge), stop recursion. 97cb93a386Sopenharmony_ci // If our branches are back-edges, we do not record them. 98cb93a386Sopenharmony_ci // We have to record crossing edges however. 99cb93a386Sopenharmony_ci if (has_visited_forward_edge(block_id)) 100cb93a386Sopenharmony_ci return true; 101cb93a386Sopenharmony_ci else if (is_back_edge(block_id)) 102cb93a386Sopenharmony_ci return false; 103cb93a386Sopenharmony_ci 104cb93a386Sopenharmony_ci // Block back-edges from recursively revisiting ourselves. 105cb93a386Sopenharmony_ci visit_order[block_id].get() = 0; 106cb93a386Sopenharmony_ci 107cb93a386Sopenharmony_ci auto &block = compiler.get<SPIRBlock>(block_id); 108cb93a386Sopenharmony_ci 109cb93a386Sopenharmony_ci // If this is a loop header, add an implied branch to the merge target. 110cb93a386Sopenharmony_ci // This is needed to avoid annoying cases with do { ... } while(false) loops often generated by inliners. 111cb93a386Sopenharmony_ci // To the CFG, this is linear control flow, but we risk picking the do/while scope as our dominating block. 112cb93a386Sopenharmony_ci // This makes sure that if we are accessing a variable outside the do/while, we choose the loop header as dominator. 113cb93a386Sopenharmony_ci // We could use has_visited_forward_edge, but this break code-gen where the merge block is unreachable in the CFG. 114cb93a386Sopenharmony_ci 115cb93a386Sopenharmony_ci // Make a point out of visiting merge target first. This is to make sure that post visit order outside the loop 116cb93a386Sopenharmony_ci // is lower than inside the loop, which is going to be key for some traversal algorithms like post-dominance analysis. 117cb93a386Sopenharmony_ci // For selection constructs true/false blocks will end up visiting the merge block directly and it works out fine, 118cb93a386Sopenharmony_ci // but for loops, only the header might end up actually branching to merge block. 119cb93a386Sopenharmony_ci if (block.merge == SPIRBlock::MergeLoop && post_order_visit(block.merge_block)) 120cb93a386Sopenharmony_ci add_branch(block_id, block.merge_block); 121cb93a386Sopenharmony_ci 122cb93a386Sopenharmony_ci // First visit our branch targets. 123cb93a386Sopenharmony_ci switch (block.terminator) 124cb93a386Sopenharmony_ci { 125cb93a386Sopenharmony_ci case SPIRBlock::Direct: 126cb93a386Sopenharmony_ci if (post_order_visit(block.next_block)) 127cb93a386Sopenharmony_ci add_branch(block_id, block.next_block); 128cb93a386Sopenharmony_ci break; 129cb93a386Sopenharmony_ci 130cb93a386Sopenharmony_ci case SPIRBlock::Select: 131cb93a386Sopenharmony_ci if (post_order_visit(block.true_block)) 132cb93a386Sopenharmony_ci add_branch(block_id, block.true_block); 133cb93a386Sopenharmony_ci if (post_order_visit(block.false_block)) 134cb93a386Sopenharmony_ci add_branch(block_id, block.false_block); 135cb93a386Sopenharmony_ci break; 136cb93a386Sopenharmony_ci 137cb93a386Sopenharmony_ci case SPIRBlock::MultiSelect: 138cb93a386Sopenharmony_ci for (auto &target : block.cases) 139cb93a386Sopenharmony_ci { 140cb93a386Sopenharmony_ci if (post_order_visit(target.block)) 141cb93a386Sopenharmony_ci add_branch(block_id, target.block); 142cb93a386Sopenharmony_ci } 143cb93a386Sopenharmony_ci if (block.default_block && post_order_visit(block.default_block)) 144cb93a386Sopenharmony_ci add_branch(block_id, block.default_block); 145cb93a386Sopenharmony_ci break; 146cb93a386Sopenharmony_ci 147cb93a386Sopenharmony_ci default: 148cb93a386Sopenharmony_ci break; 149cb93a386Sopenharmony_ci } 150cb93a386Sopenharmony_ci 151cb93a386Sopenharmony_ci // If this is a selection merge, add an implied branch to the merge target. 152cb93a386Sopenharmony_ci // This is needed to avoid cases where an inner branch dominates the outer branch. 153cb93a386Sopenharmony_ci // This can happen if one of the branches exit early, e.g.: 154cb93a386Sopenharmony_ci // if (cond) { ...; break; } else { var = 100 } use_var(var); 155cb93a386Sopenharmony_ci // We can use the variable without a Phi since there is only one possible parent here. 156cb93a386Sopenharmony_ci // However, in this case, we need to hoist out the inner variable to outside the branch. 157cb93a386Sopenharmony_ci // Use same strategy as loops. 158cb93a386Sopenharmony_ci if (block.merge == SPIRBlock::MergeSelection && post_order_visit(block.next_block)) 159cb93a386Sopenharmony_ci { 160cb93a386Sopenharmony_ci // If there is only one preceding edge to the merge block and it's not ourselves, we need a fixup. 161cb93a386Sopenharmony_ci // Add a fake branch so any dominator in either the if (), or else () block, or a lone case statement 162cb93a386Sopenharmony_ci // will be hoisted out to outside the selection merge. 163cb93a386Sopenharmony_ci // If size > 1, the variable will be automatically hoisted, so we should not mess with it. 164cb93a386Sopenharmony_ci // The exception here is switch blocks, where we can have multiple edges to merge block, 165cb93a386Sopenharmony_ci // all coming from same scope, so be more conservative in this case. 166cb93a386Sopenharmony_ci // Adding fake branches unconditionally breaks parameter preservation analysis, 167cb93a386Sopenharmony_ci // which looks at how variables are accessed through the CFG. 168cb93a386Sopenharmony_ci auto pred_itr = preceding_edges.find(block.next_block); 169cb93a386Sopenharmony_ci if (pred_itr != end(preceding_edges)) 170cb93a386Sopenharmony_ci { 171cb93a386Sopenharmony_ci auto &pred = pred_itr->second; 172cb93a386Sopenharmony_ci auto succ_itr = succeeding_edges.find(block_id); 173cb93a386Sopenharmony_ci size_t num_succeeding_edges = 0; 174cb93a386Sopenharmony_ci if (succ_itr != end(succeeding_edges)) 175cb93a386Sopenharmony_ci num_succeeding_edges = succ_itr->second.size(); 176cb93a386Sopenharmony_ci 177cb93a386Sopenharmony_ci if (block.terminator == SPIRBlock::MultiSelect && num_succeeding_edges == 1) 178cb93a386Sopenharmony_ci { 179cb93a386Sopenharmony_ci // Multiple branches can come from the same scope due to "break;", so we need to assume that all branches 180cb93a386Sopenharmony_ci // come from same case scope in worst case, even if there are multiple preceding edges. 181cb93a386Sopenharmony_ci // If we have more than one succeeding edge from the block header, it should be impossible 182cb93a386Sopenharmony_ci // to have a dominator be inside the block. 183cb93a386Sopenharmony_ci // Only case this can go wrong is if we have 2 or more edges from block header and 184cb93a386Sopenharmony_ci // 2 or more edges to merge block, and still have dominator be inside a case label. 185cb93a386Sopenharmony_ci if (!pred.empty()) 186cb93a386Sopenharmony_ci add_branch(block_id, block.next_block); 187cb93a386Sopenharmony_ci } 188cb93a386Sopenharmony_ci else 189cb93a386Sopenharmony_ci { 190cb93a386Sopenharmony_ci if (pred.size() == 1 && *pred.begin() != block_id) 191cb93a386Sopenharmony_ci add_branch(block_id, block.next_block); 192cb93a386Sopenharmony_ci } 193cb93a386Sopenharmony_ci } 194cb93a386Sopenharmony_ci else 195cb93a386Sopenharmony_ci { 196cb93a386Sopenharmony_ci // If the merge block does not have any preceding edges, i.e. unreachable, hallucinate it. 197cb93a386Sopenharmony_ci // We're going to do code-gen for it, and domination analysis requires that we have at least one preceding edge. 198cb93a386Sopenharmony_ci add_branch(block_id, block.next_block); 199cb93a386Sopenharmony_ci } 200cb93a386Sopenharmony_ci } 201cb93a386Sopenharmony_ci 202cb93a386Sopenharmony_ci // Then visit ourselves. Start counting at one, to let 0 be a magic value for testing back vs. crossing edges. 203cb93a386Sopenharmony_ci visit_order[block_id].get() = ++visit_count; 204cb93a386Sopenharmony_ci post_order.push_back(block_id); 205cb93a386Sopenharmony_ci return true; 206cb93a386Sopenharmony_ci} 207cb93a386Sopenharmony_ci 208cb93a386Sopenharmony_civoid CFG::build_post_order_visit_order() 209cb93a386Sopenharmony_ci{ 210cb93a386Sopenharmony_ci uint32_t block = func.entry_block; 211cb93a386Sopenharmony_ci visit_count = 0; 212cb93a386Sopenharmony_ci visit_order.clear(); 213cb93a386Sopenharmony_ci post_order.clear(); 214cb93a386Sopenharmony_ci post_order_visit(block); 215cb93a386Sopenharmony_ci} 216cb93a386Sopenharmony_ci 217cb93a386Sopenharmony_civoid CFG::add_branch(uint32_t from, uint32_t to) 218cb93a386Sopenharmony_ci{ 219cb93a386Sopenharmony_ci const auto add_unique = [](SmallVector<uint32_t> &l, uint32_t value) { 220cb93a386Sopenharmony_ci auto itr = find(begin(l), end(l), value); 221cb93a386Sopenharmony_ci if (itr == end(l)) 222cb93a386Sopenharmony_ci l.push_back(value); 223cb93a386Sopenharmony_ci }; 224cb93a386Sopenharmony_ci add_unique(preceding_edges[to], from); 225cb93a386Sopenharmony_ci add_unique(succeeding_edges[from], to); 226cb93a386Sopenharmony_ci} 227cb93a386Sopenharmony_ci 228cb93a386Sopenharmony_ciuint32_t CFG::find_loop_dominator(uint32_t block_id) const 229cb93a386Sopenharmony_ci{ 230cb93a386Sopenharmony_ci while (block_id != SPIRBlock::NoDominator) 231cb93a386Sopenharmony_ci { 232cb93a386Sopenharmony_ci auto itr = preceding_edges.find(block_id); 233cb93a386Sopenharmony_ci if (itr == end(preceding_edges)) 234cb93a386Sopenharmony_ci return SPIRBlock::NoDominator; 235cb93a386Sopenharmony_ci if (itr->second.empty()) 236cb93a386Sopenharmony_ci return SPIRBlock::NoDominator; 237cb93a386Sopenharmony_ci 238cb93a386Sopenharmony_ci uint32_t pred_block_id = SPIRBlock::NoDominator; 239cb93a386Sopenharmony_ci bool ignore_loop_header = false; 240cb93a386Sopenharmony_ci 241cb93a386Sopenharmony_ci // If we are a merge block, go directly to the header block. 242cb93a386Sopenharmony_ci // Only consider a loop dominator if we are branching from inside a block to a loop header. 243cb93a386Sopenharmony_ci // NOTE: In the CFG we forced an edge from header to merge block always to support variable scopes properly. 244cb93a386Sopenharmony_ci for (auto &pred : itr->second) 245cb93a386Sopenharmony_ci { 246cb93a386Sopenharmony_ci auto &pred_block = compiler.get<SPIRBlock>(pred); 247cb93a386Sopenharmony_ci if (pred_block.merge == SPIRBlock::MergeLoop && pred_block.merge_block == ID(block_id)) 248cb93a386Sopenharmony_ci { 249cb93a386Sopenharmony_ci pred_block_id = pred; 250cb93a386Sopenharmony_ci ignore_loop_header = true; 251cb93a386Sopenharmony_ci break; 252cb93a386Sopenharmony_ci } 253cb93a386Sopenharmony_ci else if (pred_block.merge == SPIRBlock::MergeSelection && pred_block.next_block == ID(block_id)) 254cb93a386Sopenharmony_ci { 255cb93a386Sopenharmony_ci pred_block_id = pred; 256cb93a386Sopenharmony_ci break; 257cb93a386Sopenharmony_ci } 258cb93a386Sopenharmony_ci } 259cb93a386Sopenharmony_ci 260cb93a386Sopenharmony_ci // No merge block means we can just pick any edge. Loop headers dominate the inner loop, so any path we 261cb93a386Sopenharmony_ci // take will lead there. 262cb93a386Sopenharmony_ci if (pred_block_id == SPIRBlock::NoDominator) 263cb93a386Sopenharmony_ci pred_block_id = itr->second.front(); 264cb93a386Sopenharmony_ci 265cb93a386Sopenharmony_ci block_id = pred_block_id; 266cb93a386Sopenharmony_ci 267cb93a386Sopenharmony_ci if (!ignore_loop_header && block_id) 268cb93a386Sopenharmony_ci { 269cb93a386Sopenharmony_ci auto &block = compiler.get<SPIRBlock>(block_id); 270cb93a386Sopenharmony_ci if (block.merge == SPIRBlock::MergeLoop) 271cb93a386Sopenharmony_ci return block_id; 272cb93a386Sopenharmony_ci } 273cb93a386Sopenharmony_ci } 274cb93a386Sopenharmony_ci 275cb93a386Sopenharmony_ci return block_id; 276cb93a386Sopenharmony_ci} 277cb93a386Sopenharmony_ci 278cb93a386Sopenharmony_cibool CFG::node_terminates_control_flow_in_sub_graph(BlockID from, BlockID to) const 279cb93a386Sopenharmony_ci{ 280cb93a386Sopenharmony_ci // Walk backwards, starting from "to" block. 281cb93a386Sopenharmony_ci // Only follow pred edges if they have a 1:1 relationship, or a merge relationship. 282cb93a386Sopenharmony_ci // If we cannot find a path to "from", we must assume that to is inside control flow in some way. 283cb93a386Sopenharmony_ci 284cb93a386Sopenharmony_ci auto &from_block = compiler.get<SPIRBlock>(from); 285cb93a386Sopenharmony_ci BlockID ignore_block_id = 0; 286cb93a386Sopenharmony_ci if (from_block.merge == SPIRBlock::MergeLoop) 287cb93a386Sopenharmony_ci ignore_block_id = from_block.merge_block; 288cb93a386Sopenharmony_ci 289cb93a386Sopenharmony_ci while (to != from) 290cb93a386Sopenharmony_ci { 291cb93a386Sopenharmony_ci auto pred_itr = preceding_edges.find(to); 292cb93a386Sopenharmony_ci if (pred_itr == end(preceding_edges)) 293cb93a386Sopenharmony_ci return false; 294cb93a386Sopenharmony_ci 295cb93a386Sopenharmony_ci DominatorBuilder builder(*this); 296cb93a386Sopenharmony_ci for (auto &edge : pred_itr->second) 297cb93a386Sopenharmony_ci builder.add_block(edge); 298cb93a386Sopenharmony_ci 299cb93a386Sopenharmony_ci uint32_t dominator = builder.get_dominator(); 300cb93a386Sopenharmony_ci if (dominator == 0) 301cb93a386Sopenharmony_ci return false; 302cb93a386Sopenharmony_ci 303cb93a386Sopenharmony_ci auto &dom = compiler.get<SPIRBlock>(dominator); 304cb93a386Sopenharmony_ci 305cb93a386Sopenharmony_ci bool true_path_ignore = false; 306cb93a386Sopenharmony_ci bool false_path_ignore = false; 307cb93a386Sopenharmony_ci if (ignore_block_id && dom.terminator == SPIRBlock::Select) 308cb93a386Sopenharmony_ci { 309cb93a386Sopenharmony_ci auto &true_block = compiler.get<SPIRBlock>(dom.true_block); 310cb93a386Sopenharmony_ci auto &false_block = compiler.get<SPIRBlock>(dom.false_block); 311cb93a386Sopenharmony_ci auto &ignore_block = compiler.get<SPIRBlock>(ignore_block_id); 312cb93a386Sopenharmony_ci true_path_ignore = compiler.execution_is_branchless(true_block, ignore_block); 313cb93a386Sopenharmony_ci false_path_ignore = compiler.execution_is_branchless(false_block, ignore_block); 314cb93a386Sopenharmony_ci } 315cb93a386Sopenharmony_ci 316cb93a386Sopenharmony_ci if ((dom.merge == SPIRBlock::MergeSelection && dom.next_block == to) || 317cb93a386Sopenharmony_ci (dom.merge == SPIRBlock::MergeLoop && dom.merge_block == to) || 318cb93a386Sopenharmony_ci (dom.terminator == SPIRBlock::Direct && dom.next_block == to) || 319cb93a386Sopenharmony_ci (dom.terminator == SPIRBlock::Select && dom.true_block == to && false_path_ignore) || 320cb93a386Sopenharmony_ci (dom.terminator == SPIRBlock::Select && dom.false_block == to && true_path_ignore)) 321cb93a386Sopenharmony_ci { 322cb93a386Sopenharmony_ci // Allow walking selection constructs if the other branch reaches out of a loop construct. 323cb93a386Sopenharmony_ci // It cannot be in-scope anymore. 324cb93a386Sopenharmony_ci to = dominator; 325cb93a386Sopenharmony_ci } 326cb93a386Sopenharmony_ci else 327cb93a386Sopenharmony_ci return false; 328cb93a386Sopenharmony_ci } 329cb93a386Sopenharmony_ci 330cb93a386Sopenharmony_ci return true; 331cb93a386Sopenharmony_ci} 332cb93a386Sopenharmony_ci 333cb93a386Sopenharmony_ciDominatorBuilder::DominatorBuilder(const CFG &cfg_) 334cb93a386Sopenharmony_ci : cfg(cfg_) 335cb93a386Sopenharmony_ci{ 336cb93a386Sopenharmony_ci} 337cb93a386Sopenharmony_ci 338cb93a386Sopenharmony_civoid DominatorBuilder::add_block(uint32_t block) 339cb93a386Sopenharmony_ci{ 340cb93a386Sopenharmony_ci if (!cfg.get_immediate_dominator(block)) 341cb93a386Sopenharmony_ci { 342cb93a386Sopenharmony_ci // Unreachable block via the CFG, we will never emit this code anyways. 343cb93a386Sopenharmony_ci return; 344cb93a386Sopenharmony_ci } 345cb93a386Sopenharmony_ci 346cb93a386Sopenharmony_ci if (!dominator) 347cb93a386Sopenharmony_ci { 348cb93a386Sopenharmony_ci dominator = block; 349cb93a386Sopenharmony_ci return; 350cb93a386Sopenharmony_ci } 351cb93a386Sopenharmony_ci 352cb93a386Sopenharmony_ci if (block != dominator) 353cb93a386Sopenharmony_ci dominator = cfg.find_common_dominator(block, dominator); 354cb93a386Sopenharmony_ci} 355cb93a386Sopenharmony_ci 356cb93a386Sopenharmony_civoid DominatorBuilder::lift_continue_block_dominator() 357cb93a386Sopenharmony_ci{ 358cb93a386Sopenharmony_ci // It is possible for a continue block to be the dominator of a variable is only accessed inside the while block of a do-while loop. 359cb93a386Sopenharmony_ci // We cannot safely declare variables inside a continue block, so move any variable declared 360cb93a386Sopenharmony_ci // in a continue block to the entry block to simplify. 361cb93a386Sopenharmony_ci // It makes very little sense for a continue block to ever be a dominator, so fall back to the simplest 362cb93a386Sopenharmony_ci // solution. 363cb93a386Sopenharmony_ci 364cb93a386Sopenharmony_ci if (!dominator) 365cb93a386Sopenharmony_ci return; 366cb93a386Sopenharmony_ci 367cb93a386Sopenharmony_ci auto &block = cfg.get_compiler().get<SPIRBlock>(dominator); 368cb93a386Sopenharmony_ci auto post_order = cfg.get_visit_order(dominator); 369cb93a386Sopenharmony_ci 370cb93a386Sopenharmony_ci // If we are branching to a block with a higher post-order traversal index (continue blocks), we have a problem 371cb93a386Sopenharmony_ci // since we cannot create sensible GLSL code for this, fallback to entry block. 372cb93a386Sopenharmony_ci bool back_edge_dominator = false; 373cb93a386Sopenharmony_ci switch (block.terminator) 374cb93a386Sopenharmony_ci { 375cb93a386Sopenharmony_ci case SPIRBlock::Direct: 376cb93a386Sopenharmony_ci if (cfg.get_visit_order(block.next_block) > post_order) 377cb93a386Sopenharmony_ci back_edge_dominator = true; 378cb93a386Sopenharmony_ci break; 379cb93a386Sopenharmony_ci 380cb93a386Sopenharmony_ci case SPIRBlock::Select: 381cb93a386Sopenharmony_ci if (cfg.get_visit_order(block.true_block) > post_order) 382cb93a386Sopenharmony_ci back_edge_dominator = true; 383cb93a386Sopenharmony_ci if (cfg.get_visit_order(block.false_block) > post_order) 384cb93a386Sopenharmony_ci back_edge_dominator = true; 385cb93a386Sopenharmony_ci break; 386cb93a386Sopenharmony_ci 387cb93a386Sopenharmony_ci case SPIRBlock::MultiSelect: 388cb93a386Sopenharmony_ci for (auto &target : block.cases) 389cb93a386Sopenharmony_ci { 390cb93a386Sopenharmony_ci if (cfg.get_visit_order(target.block) > post_order) 391cb93a386Sopenharmony_ci back_edge_dominator = true; 392cb93a386Sopenharmony_ci } 393cb93a386Sopenharmony_ci if (block.default_block && cfg.get_visit_order(block.default_block) > post_order) 394cb93a386Sopenharmony_ci back_edge_dominator = true; 395cb93a386Sopenharmony_ci break; 396cb93a386Sopenharmony_ci 397cb93a386Sopenharmony_ci default: 398cb93a386Sopenharmony_ci break; 399cb93a386Sopenharmony_ci } 400cb93a386Sopenharmony_ci 401cb93a386Sopenharmony_ci if (back_edge_dominator) 402cb93a386Sopenharmony_ci dominator = cfg.get_function().entry_block; 403cb93a386Sopenharmony_ci} 404cb93a386Sopenharmony_ci} // namespace SPIRV_CROSS_NAMESPACE 405