From 889f29c18a89ce52001736a61d41fbdf6120932d Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Sun, 7 Jun 2015 15:14:14 -0700 Subject: [PATCH] Fixing CFG to remove unreachable blocks and properly maintain dominators. --- .../control_flow_simplification_pass.cc | 14 ++- src/xenia/cpu/frontend/ppc_translator.cc | 1 - src/xenia/cpu/hir/hir_builder.cc | 103 +++++++++++++++++- src/xenia/cpu/hir/hir_builder.h | 3 + 4 files changed, 116 insertions(+), 5 deletions(-) diff --git a/src/xenia/cpu/compiler/passes/control_flow_simplification_pass.cc b/src/xenia/cpu/compiler/passes/control_flow_simplification_pass.cc index 7841d9a8c..3e1af8fa0 100644 --- a/src/xenia/cpu/compiler/passes/control_flow_simplification_pass.cc +++ b/src/xenia/cpu/compiler/passes/control_flow_simplification_pass.cc @@ -31,9 +31,21 @@ ControlFlowSimplificationPass::ControlFlowSimplificationPass() ControlFlowSimplificationPass::~ControlFlowSimplificationPass() {} bool ControlFlowSimplificationPass::Run(HIRBuilder* builder) { + // Walk forwards and kill any unreachable blocks. + // Do this before merging. + auto block = builder->first_block(); + while (block) { + auto next_block = block->next; + if (!block->incoming_edge_head && block->prev) { + // Block is in the interior and has no incoming edges - kill it. + builder->RemoveBlock(block); + } + block = next_block; + } + // Walk backwards and merge blocks if possible. bool merged_any = false; - auto block = builder->last_block(); + block = builder->last_block(); while (block) { auto prev_block = block->prev; const uint32_t expected = Edge::DOMINATES | Edge::UNCONDITIONAL; diff --git a/src/xenia/cpu/frontend/ppc_translator.cc b/src/xenia/cpu/frontend/ppc_translator.cc index e8a552675..a606e8227 100644 --- a/src/xenia/cpu/frontend/ppc_translator.cc +++ b/src/xenia/cpu/frontend/ppc_translator.cc @@ -50,7 +50,6 @@ PPCTranslator::PPCTranslator(PPCFrontend* frontend) : frontend_(frontend) { // The CFG is required for simplification and dirtied by it. compiler_->AddPass(std::make_unique()); compiler_->AddPass(std::make_unique()); - compiler_->AddPass(std::make_unique()); // Passes are executed in the order they are added. Multiple of the same // pass type may be used. diff --git a/src/xenia/cpu/hir/hir_builder.cc b/src/xenia/cpu/hir/hir_builder.cc index e23ff610f..c5eeccfc5 100644 --- a/src/xenia/cpu/hir/hir_builder.cc +++ b/src/xenia/cpu/hir/hir_builder.cc @@ -379,6 +379,8 @@ void HIRBuilder::InsertLabel(Label* label, Instr* prev_instr) { Block* next_block = prev_instr->block->next; Block* new_block = arena_->Alloc(); + new_block->ordinal = -1; + new_block->incoming_values = nullptr; new_block->arena = arena_; new_block->prev = prev_block; new_block->next = next_block; @@ -433,16 +435,96 @@ void HIRBuilder::ResetLabelTags() { } void HIRBuilder::AddEdge(Block* src, Block* dest, uint32_t flags) { + bool dest_was_dominated = + dest->incoming_edge_head && !dest->incoming_edge_head->incoming_next; + Edge* edge = arena_->Alloc(); edge->src = src; edge->dest = dest; edge->flags = flags; - edge->outgoing_prev = NULL; + edge->outgoing_prev = nullptr; edge->outgoing_next = src->outgoing_edge_head; + if (edge->outgoing_next) { + edge->outgoing_next->outgoing_prev = edge; + } src->outgoing_edge_head = edge; - edge->incoming_prev = NULL; + edge->incoming_prev = nullptr; edge->incoming_next = dest->incoming_edge_head; + if (edge->incoming_next) { + edge->incoming_next->incoming_prev = edge; + } dest->incoming_edge_head = edge; + + if (dest_was_dominated) { + // If dest was previously dominated it no longer is. + auto edge = dest->incoming_edge_head; + while (edge) { + edge->flags &= ~Edge::DOMINATES; + edge = edge->incoming_next; + } + } +} + +void HIRBuilder::RemoveEdge(Block* src, Block* dest) { + auto edge = src->outgoing_edge_head; + while (edge) { + if (edge->dest == dest) { + RemoveEdge(edge); + break; + } + edge = edge->outgoing_next; + } +} + +void HIRBuilder::RemoveEdge(Edge* edge) { + if (edge->outgoing_prev) { + edge->outgoing_prev->outgoing_next = edge->outgoing_next; + } + if (edge->outgoing_next) { + edge->outgoing_next->outgoing_prev = edge->outgoing_prev; + } + if (edge == edge->src->outgoing_edge_head) { + edge->src->outgoing_edge_head = edge->outgoing_next; + } + if (edge->incoming_prev) { + edge->incoming_prev->incoming_next = edge->incoming_next; + } + if (edge->incoming_next) { + edge->incoming_next->incoming_prev = edge->incoming_prev; + } + if (edge == edge->dest->incoming_edge_head) { + edge->dest->incoming_edge_head = edge->incoming_next; + } + edge->incoming_next = edge->incoming_prev = nullptr; + edge->outgoing_next = edge->outgoing_prev = nullptr; + + if (edge->dest->incoming_edge_head && + !edge->dest->incoming_edge_head->incoming_next) { + // Dest is now dominated by the last remaining edge. + edge->dest->incoming_edge_head->flags |= Edge::DOMINATES; + } +} + +void HIRBuilder::RemoveBlock(Block* block) { + while (block->incoming_edge_head) { + RemoveEdge(block->incoming_edge_head); + } + while (block->outgoing_edge_head) { + RemoveEdge(block->outgoing_edge_head); + } + if (block->prev) { + block->prev->next = block->next; + } + if (block->next) { + block->next->prev = block->prev; + } + if (block == block_head_) { + block_head_ = block->next; + } + if (block == block_tail_) { + block_tail_ = block->prev; + } + block->next = block->prev = nullptr; } void HIRBuilder::MergeAdjacentBlocks(Block* left, Block* right) { @@ -481,7 +563,7 @@ void HIRBuilder::MergeAdjacentBlocks(Block* left, Block* right) { } left->instr_tail = instr; - // Unlink from old block list; + // Unlink from old block list. right->instr_head = next; if (right->instr_tail == instr) { right->instr_tail = nullptr; @@ -517,6 +599,19 @@ void HIRBuilder::MergeAdjacentBlocks(Block* left, Block* right) { label->next = nullptr; } + // Remove edge between left and right. + RemoveEdge(left, right); + + // Move right's outgoing edges to left. + assert_null(right->incoming_edge_head); + auto edge = right->outgoing_edge_head; + while (edge) { + auto next_edge = edge->outgoing_next; + RemoveEdge(edge); + AddEdge(left, edge->dest, edge->flags); + edge = next_edge; + } + // Remove the right block from the block list. left->next = right->next; if (right->next) { @@ -529,6 +624,8 @@ void HIRBuilder::MergeAdjacentBlocks(Block* left, Block* right) { Block* HIRBuilder::AppendBlock() { Block* block = arena_->Alloc(); + block->ordinal = -1; + block->incoming_values = nullptr; block->arena = arena_; block->next = NULL; block->prev = block_tail_; diff --git a/src/xenia/cpu/hir/hir_builder.h b/src/xenia/cpu/hir/hir_builder.h index c72983c14..36002ff89 100644 --- a/src/xenia/cpu/hir/hir_builder.h +++ b/src/xenia/cpu/hir/hir_builder.h @@ -60,6 +60,9 @@ class HIRBuilder { void ResetLabelTags(); void AddEdge(Block* src, Block* dest, uint32_t flags); + void RemoveEdge(Block* src, Block* dest); + void RemoveEdge(Edge* edge); + void RemoveBlock(Block* block); void MergeAdjacentBlocks(Block* left, Block* right); // static allocations: