Fixing CFG to remove unreachable blocks and properly maintain dominators.

This commit is contained in:
Ben Vanik 2015-06-07 15:14:14 -07:00
parent 10e8d0bd9b
commit 889f29c18a
4 changed files with 116 additions and 5 deletions

View File

@ -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;

View File

@ -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<passes::ControlFlowAnalysisPass>());
compiler_->AddPass(std::make_unique<passes::ControlFlowSimplificationPass>());
compiler_->AddPass(std::make_unique<passes::ControlFlowAnalysisPass>());
// Passes are executed in the order they are added. Multiple of the same
// pass type may be used.

View File

@ -379,6 +379,8 @@ void HIRBuilder::InsertLabel(Label* label, Instr* prev_instr) {
Block* next_block = prev_instr->block->next;
Block* new_block = arena_->Alloc<Block>();
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>();
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>();
block->ordinal = -1;
block->incoming_values = nullptr;
block->arena = arena_;
block->next = NULL;
block->prev = block_tail_;

View File

@ -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: