/* * Copyright © 2018 Valve Corporation * * SPDX-License-Identifier: MIT */ #ifndef ACO_DOMINANCE_CPP #define ACO_DOMINANCE_CPP #include "aco_ir.h" /* * Implements the algorithms for computing the dominator tree from * "A Simple, Fast Dominance Algorithm" by Cooper, Harvey, and Kennedy. * * Different from the paper, our CFG allows to compute the dominator tree * in a single pass as it is guaranteed that the dominating predecessors * are processed before the current block. */ namespace aco { namespace { struct block_dom_info { uint32_t logical_descendants = 0; uint32_t linear_descendants = 0; uint32_t logical_depth = 0; uint32_t linear_depth = 0; small_vec logical_children; small_vec linear_children; }; void calc_indices(Program* program) { std::vector info(program->blocks.size()); /* Create the linear and logical dominance trees. Calculating logical_descendants and * linear_descendants requires no recursion because the immediate dominator of each block has a * lower index. */ for (int i = program->blocks.size() - 1; i >= 0; i--) { Block& block = program->blocks[i]; /* Add this as a child node of the parent. */ if (block.logical_idom != i && block.logical_idom != -1) { assert(i > block.logical_idom); info[block.logical_idom].logical_children.push_back(i); /* Add this node's descendants and itself to the parent. */ info[block.logical_idom].logical_descendants += info[i].logical_descendants + 1; } if (block.linear_idom != i) { assert(i > block.linear_idom); info[block.linear_idom].linear_children.push_back(i); info[block.linear_idom].linear_descendants += info[i].linear_descendants + 1; } } /* Fill in the indices that would be obtained in a preorder and postorder traversal of the * dominance trees. */ for (unsigned i = 0; i < program->blocks.size(); i++) { Block& block = program->blocks[i]; /* Because of block_kind_resume, the root node's indices start at the block index to avoid * reusing indices. */ if (block.logical_idom == (int)i) block.logical_dom_pre_index = i; if (block.linear_idom == (int)i) block.linear_dom_pre_index = i; /* Visit each child and assign it's preorder indices and depth. */ unsigned start = block.logical_dom_pre_index + 1; for (unsigned j = 0; j < info[i].logical_children.size(); j++) { unsigned child = info[i].logical_children[j]; info[child].logical_depth = info[i].logical_depth + 1; program->blocks[child].logical_dom_pre_index = start; start += info[child].logical_descendants + 1; } start = block.linear_dom_pre_index + 1; for (unsigned j = 0; j < info[i].linear_children.size(); j++) { unsigned child = info[i].linear_children[j]; info[child].linear_depth = info[i].linear_depth + 1; program->blocks[child].linear_dom_pre_index = start; start += info[child].linear_descendants + 1; } /* The postorder traversal is the same as the preorder traversal, except that when this block * is visited, we haven't visited it's ancestors and have already visited it's descendants. * This means that the postorder_index is preorder_index-depth+descendants. */ block.logical_dom_post_index = block.logical_dom_pre_index - info[i].logical_depth + info[i].logical_descendants; block.linear_dom_post_index = block.linear_dom_pre_index - info[i].linear_depth + info[i].linear_descendants; } } } /* end namespace */ void dominator_tree(Program* program) { for (unsigned i = 0; i < program->blocks.size(); i++) { Block& block = program->blocks[i]; /* If this block has no predecessor, it dominates itself by definition */ if (block.linear_preds.empty()) { block.linear_idom = block.index; block.logical_idom = block.index; continue; } int new_logical_idom = -1; int new_linear_idom = -1; for (unsigned pred_idx : block.logical_preds) { if ((int)program->blocks[pred_idx].logical_idom == -1) continue; if (new_logical_idom == -1) { new_logical_idom = pred_idx; continue; } while ((int)pred_idx != new_logical_idom) { if ((int)pred_idx > new_logical_idom) pred_idx = program->blocks[pred_idx].logical_idom; if ((int)pred_idx < new_logical_idom) new_logical_idom = program->blocks[new_logical_idom].logical_idom; } } for (unsigned pred_idx : block.linear_preds) { if ((int)program->blocks[pred_idx].linear_idom == -1) continue; if (new_linear_idom == -1) { new_linear_idom = pred_idx; continue; } while ((int)pred_idx != new_linear_idom) { if ((int)pred_idx > new_linear_idom) pred_idx = program->blocks[pred_idx].linear_idom; if ((int)pred_idx < new_linear_idom) new_linear_idom = program->blocks[new_linear_idom].linear_idom; } } block.logical_idom = new_logical_idom; block.linear_idom = new_linear_idom; } calc_indices(program); } } // namespace aco #endif