Commit 713278ab by Andrew Scull

Remove jumps over empty blocks.

After contracting a node reorder it as unreachable even if it hasn't yet been placed. BUG= R=jvoung@chromium.org, jvoung, stichnot Review URL: https://codereview.chromium.org/1246173003.
parent 0dab0324
...@@ -259,6 +259,8 @@ void Cfg::advancedPhiLowering() { ...@@ -259,6 +259,8 @@ void Cfg::advancedPhiLowering() {
// placed, while maintaining the same relative ordering among already // placed, while maintaining the same relative ordering among already
// placed nodes. // placed nodes.
void Cfg::reorderNodes() { void Cfg::reorderNodes() {
// TODO(ascull): it would be nice if the switch tests were always followed
// by the default case to allow for fall through.
typedef std::list<CfgNode *> PlacedList; typedef std::list<CfgNode *> PlacedList;
PlacedList Placed; // Nodes with relative placement locked down PlacedList Placed; // Nodes with relative placement locked down
PlacedList Unreachable; // Unreachable nodes PlacedList Unreachable; // Unreachable nodes
...@@ -271,20 +273,21 @@ void Cfg::reorderNodes() { ...@@ -271,20 +273,21 @@ void Cfg::reorderNodes() {
// --PlaceIndex and assert() statements before moving to the next // --PlaceIndex and assert() statements before moving to the next
// node. // node.
do { do {
if (!Node->needsPlacement()) {
// Add to the end of the Placed list.
Placed.push_back(Node);
PlaceIndex[Node->getIndex()] = Placed.end();
continue;
}
Node->setNeedsPlacement(false);
if (Node != getEntryNode() && Node->getInEdges().empty()) { if (Node != getEntryNode() && Node->getInEdges().empty()) {
// The node has essentially been deleted since it is not a // The node has essentially been deleted since it is not a
// successor of any other node. // successor of any other node.
Unreachable.push_back(Node); Unreachable.push_back(Node);
PlaceIndex[Node->getIndex()] = Unreachable.end(); PlaceIndex[Node->getIndex()] = Unreachable.end();
Node->setNeedsPlacement(false);
continue; continue;
} }
if (!Node->needsPlacement()) {
// Add to the end of the Placed list.
Placed.push_back(Node);
PlaceIndex[Node->getIndex()] = Placed.end();
continue;
}
Node->setNeedsPlacement(false);
// Assume for now that the unplaced node is from edge-splitting // Assume for now that the unplaced node is from edge-splitting
// and therefore has 1 in-edge and 1 out-edge (actually, possibly // and therefore has 1 in-edge and 1 out-edge (actually, possibly
// more than 1 in-edge if the predecessor node was contracted). // more than 1 in-edge if the predecessor node was contracted).
...@@ -521,8 +524,7 @@ void Cfg::contractEmptyNodes() { ...@@ -521,8 +524,7 @@ void Cfg::contractEmptyNodes() {
void Cfg::doBranchOpt() { void Cfg::doBranchOpt() {
TimerMarker T(TimerStack::TT_doBranchOpt, this); TimerMarker T(TimerStack::TT_doBranchOpt, this);
for (auto I = Nodes.begin(), E = Nodes.end(); I != E; ++I) { for (auto I = Nodes.begin(), E = Nodes.end(); I != E; ++I) {
auto NextNode = I; auto NextNode = I + 1;
++NextNode;
(*I)->doBranchOpt(NextNode == E ? nullptr : *NextNode); (*I)->doBranchOpt(NextNode == E ? nullptr : *NextNode);
} }
} }
......
...@@ -730,41 +730,46 @@ void CfgNode::contractIfEmpty() { ...@@ -730,41 +730,46 @@ void CfgNode::contractIfEmpty() {
} }
Branch->setDeleted(); Branch->setDeleted();
assert(OutEdges.size() == 1); assert(OutEdges.size() == 1);
CfgNode *Successor = OutEdges.front();
// Repoint all this node's in-edges to this node's successor, unless // Repoint all this node's in-edges to this node's successor, unless
// this node's successor is actually itself (in which case the // this node's successor is actually itself (in which case the
// statement "OutEdges.front()->InEdges.push_back(Pred)" could // statement "OutEdges.front()->InEdges.push_back(Pred)" could
// invalidate the iterator over this->InEdges). // invalidate the iterator over this->InEdges).
if (OutEdges.front() != this) { if (Successor != this) {
for (CfgNode *Pred : InEdges) { for (CfgNode *Pred : InEdges) {
for (auto I = Pred->OutEdges.begin(), E = Pred->OutEdges.end(); I != E; for (CfgNode *&I : Pred->OutEdges) {
++I) { if (I == this) {
if (*I == this) { I = Successor;
*I = OutEdges.front(); Successor->InEdges.push_back(Pred);
OutEdges.front()->InEdges.push_back(Pred);
} }
} }
for (Inst &I : Pred->getInsts()) { for (Inst &I : Pred->getInsts()) {
if (!I.isDeleted()) if (!I.isDeleted())
I.repointEdges(this, OutEdges.front()); I.repointEdges(this, Successor);
} }
} }
// Remove the in-edge to the successor to allow node reordering to make
// better decisions. For example it's more helpful to place a node after
// a reachable predecessor than an unreachable one (like the one we just
// contracted).
Successor->InEdges.erase(
std::find(Successor->InEdges.begin(), Successor->InEdges.end(), this));
} }
InEdges.clear(); InEdges.clear();
// Don't bother removing the single out-edge, which would also
// require finding the corresponding in-edge in the successor and
// removing it.
} }
void CfgNode::doBranchOpt(const CfgNode *NextNode) { void CfgNode::doBranchOpt(const CfgNode *NextNode) {
TargetLowering *Target = Func->getTarget(); TargetLowering *Target = Func->getTarget();
// Check every instruction for a branch optimization opportunity. // Find the first opportunity for branch optimization (which will be the last
// It may be more efficient to iterate in reverse and stop after the // instruction in the block) and stop. This is sufficient unless there is some
// first opportunity, unless there is some target lowering where we // target lowering where we have the possibility of multiple optimizations per
// have the possibility of multiple such optimizations per block // block. Take care with switch lowering as there are multiple unconditional
// (currently not the case for x86 lowering). // branches and only the last can be deleted.
for (Inst &I : Insts) { for (Inst &I : reverse_range(Insts)) {
if (!I.isDeleted()) { if (!I.isDeleted()) {
Target->doBranchOpt(&I, NextNode); Target->doBranchOpt(&I, NextNode);
return;
} }
} }
} }
......
...@@ -143,7 +143,6 @@ return: ...@@ -143,7 +143,6 @@ return:
; CHECK-NEXT: je ; CHECK-NEXT: je
; CHECK-NEXT: cmp {{.*}},0xea ; CHECK-NEXT: cmp {{.*}},0xea
; CHECK-NEXT: je ; CHECK-NEXT: je
; CHECK-NEXT: jmp
; Test for correct 64-bit lowering. ; Test for correct 64-bit lowering.
; TODO(ascull): this should generate better code like the 32-bit version ; TODO(ascull): this should generate better code like the 32-bit version
......
...@@ -169,3 +169,37 @@ target: ...@@ -169,3 +169,37 @@ target:
; ARM32OM1: bx lr ; ARM32OM1: bx lr
; ARM32OM1: bl ; ARM32OM1: bl
; ARM32OM1: bx lr ; ARM32OM1: bx lr
; Unconditional branches to the block after a contracted block should be
; removed.
define void @testUncondToBlockAfterContract() {
entry:
call void @dummy()
br label %target
contract:
br label %target
target:
call void @dummy()
ret void
}
; O2-LABEL: testUncondToBlockAfterContract
; O2: call
; There will be nops for bundle align to end (for NaCl), but there should
; not be a branch.
; O2-NOT: j
; O2: call
; OM1-LABEL: testUncondToBlockAfterContract
; OM1: call
; OM1-NEXT: jmp
; OM1: call
; ARM32O2-LABEL: testUncondToBlockAfterContract
; ARM32O2: bl {{.*}} dummy
; ARM32O2-NEXT: bl {{.*}} dummy
; ARM32OM1-LABEL: testUncondToBlockAfterContract
; ARM32OM1: bl {{.*}} dummy
; ARM32OM1-NEXT: b
; ARM32OM1-NEXT: bl {{.*}} dummy
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment