Module: Mesa Branch: main Commit: c0ecc0d70bf3f46a38482cb8184cca0e367e7037 URL: http://cgit.freedesktop.org/mesa/mesa/commit/?id=c0ecc0d70bf3f46a38482cb8184cca0e367e7037
Author: Ian Romanick <ian.d.roman...@intel.com> Date: Mon Sep 11 11:29:12 2023 -0700 intel/compiler: Don't promote CFG link types when removing a block Imagine 3 blocks A, B, and C. A has a physical link to B, and B has a logical link to C. Previous to this commit, if B were removed, A would get a logical link to C. This is not correct. This was specifically observed to occur when block A was a DO block and B was the WHILE block. The DO block would have two logical successors, and that is completely invalid. v2: Assert that the links from A-to-B and B-back-to-A are the same kind. Suggested by Caio. v3: Assume the successor and predecessor lists are well formed. Use this to simplify the logic. Suggested by Caio. Add checks to cfg_t::validate to ensure the lists are well formed. v4: Remove (now unused) bblock_link_invalid. Suggested by Curro. Reviewed-by: Caio Oliveira <caio.olive...@intel.com> Reviewed-by: Francisco Jerez <curroje...@riseup.net> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/25216> --- src/intel/compiler/brw_cfg.cpp | 69 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/src/intel/compiler/brw_cfg.cpp b/src/intel/compiler/brw_cfg.cpp index 98a58150e3c..c6a384cafb7 100644 --- a/src/intel/compiler/brw_cfg.cpp +++ b/src/intel/compiler/brw_cfg.cpp @@ -439,18 +439,28 @@ void cfg_t::remove_block(bblock_t *block) { foreach_list_typed_safe (bblock_link, predecessor, link, &block->parents) { + /* cfg_t::validate checks that predecessor and successor lists are well + * formed, so it is known that the loop here would find exactly one + * block. Set old_link_kind to silence "variable used but not set" + * warnings. + */ + bblock_link_kind old_link_kind = bblock_link_logical; + /* Remove block from all of its predecessors' successor lists. */ foreach_list_typed_safe (bblock_link, successor, link, &predecessor->block->children) { if (block == successor->block) { + old_link_kind = successor->kind; successor->link.remove(); ralloc_free(successor); + break; } } /* Add removed-block's successors to its predecessors' successor lists. */ foreach_list_typed (bblock_link, successor, link, &block->children) { bool need_to_link = true; + bblock_link_kind new_link_kind = MAX2(old_link_kind, successor->kind); foreach_list_typed_safe (bblock_link, child, link, &predecessor->block->children) { /* There is already a link between the two blocks. If the links @@ -462,7 +472,7 @@ cfg_t::remove_block(bblock_t *block) * kind and the proposed link kind. */ if (child->block == successor->block) { - child->kind = MIN2(child->kind, successor->kind); + child->kind = MIN2(child->kind, new_link_kind); need_to_link = false; break; } @@ -471,16 +481,24 @@ cfg_t::remove_block(bblock_t *block) if (need_to_link) { predecessor->block->children.push_tail(link(mem_ctx, successor->block, - successor->kind)); + new_link_kind)); } } } foreach_list_typed_safe (bblock_link, successor, link, &block->children) { + /* cfg_t::validate checks that predecessor and successor lists are well + * formed, so it is known that the loop here would find exactly one + * block. Set old_link_kind to silence "variable used but not set" + * warnings. + */ + bblock_link_kind old_link_kind = bblock_link_logical; + /* Remove block from all of its childrens' parents lists. */ foreach_list_typed_safe (bblock_link, predecessor, link, &successor->block->parents) { if (block == predecessor->block) { + old_link_kind = predecessor->kind; predecessor->link.remove(); ralloc_free(predecessor); } @@ -489,6 +507,7 @@ cfg_t::remove_block(bblock_t *block) /* Add removed-block's predecessors to its successors' predecessor lists. */ foreach_list_typed (bblock_link, predecessor, link, &block->parents) { bool need_to_link = true; + bblock_link_kind new_link_kind = MAX2(old_link_kind, predecessor->kind); foreach_list_typed_safe (bblock_link, parent, link, &successor->block->parents) { /* There is already a link between the two blocks. If the links @@ -500,7 +519,7 @@ cfg_t::remove_block(bblock_t *block) * kind and the proposed link kind. */ if (parent->block == predecessor->block) { - parent->kind = MIN2(parent->kind, predecessor->kind); + parent->kind = MIN2(parent->kind, new_link_kind); need_to_link = false; break; } @@ -509,7 +528,7 @@ cfg_t::remove_block(bblock_t *block) if (need_to_link) { successor->block->parents.push_tail(link(mem_ctx, predecessor->block, - predecessor->kind)); + new_link_kind)); } } } @@ -696,11 +715,20 @@ cfg_t::validate(const char *stage_abbrev) foreach_list_typed(bblock_link, predecessor, link, &succ_block->parents) { if (predecessor->block == block) { cfgv_assert(!successor_links_back_to_predecessor); + cfgv_assert(successor->kind == predecessor->kind); successor_links_back_to_predecessor = true; } } cfgv_assert(successor_links_back_to_predecessor); + + /* Each successor block must appear only once in the list of + * successors. + */ + foreach_list_typed_from(bblock_link, later_successor, link, + &block->children, successor->link.next) { + cfgv_assert(successor->block != later_successor->block); + } } foreach_list_typed(bblock_link, predecessor, link, &block->parents) { @@ -713,11 +741,44 @@ cfg_t::validate(const char *stage_abbrev) foreach_list_typed(bblock_link, successor, link, &pred_block->children) { if (successor->block == block) { cfgv_assert(!predecessor_links_back_to_successor); + cfgv_assert(successor->kind == predecessor->kind); predecessor_links_back_to_successor = true; } } cfgv_assert(predecessor_links_back_to_successor); + + /* Each precessor block must appear only once in the list of + * precessors. + */ + foreach_list_typed_from(bblock_link, later_precessor, link, + &block->parents, predecessor->link.next) { + cfgv_assert(predecessor->block != later_precessor->block); + } + } + + backend_instruction *first_inst = block->start(); + if (first_inst->opcode == BRW_OPCODE_DO) { + /* A block starting with DO should have exactly two successors. One + * is a physical link to the block starting after the WHILE + * instruction. The other is a logical link to the block starting the + * body of the loop. + */ + bblock_t *physical_block = nullptr; + bblock_t *logical_block = nullptr; + + foreach_list_typed(bblock_link, child, link, &block->children) { + if (child->kind == bblock_link_physical) { + cfgv_assert(physical_block == nullptr); + physical_block = child->block; + } else { + cfgv_assert(logical_block == nullptr); + logical_block = child->block; + } + } + + cfgv_assert(logical_block != nullptr); + cfgv_assert(physical_block != nullptr); } } }