Skip to content

Commit

Permalink
Eliminate switch cases that can never match, for example removes all
Browse files Browse the repository at this point in the history
negative switch cases if the branch condition is known to be positive.
Inspired by a recent improvement to GCC's VRP.


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@152405 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
CunningBaldrick committed Mar 9, 2012
1 parent 7415659 commit 6f1d799
Show file tree
Hide file tree
Showing 2 changed files with 186 additions and 1 deletion.
86 changes: 86 additions & 0 deletions lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ namespace {
bool processPHI(PHINode *P);
bool processMemAccess(Instruction *I);
bool processCmp(CmpInst *C);
bool processSwitch(SwitchInst *SI);

public:
static char ID;
Expand Down Expand Up @@ -173,6 +174,84 @@ bool CorrelatedValuePropagation::processCmp(CmpInst *C) {
return true;
}

/// processSwitch - Simplify a switch instruction by removing cases which can
/// never fire. If the uselessness of a case could be determined locally then
/// constant propagation would already have figured it out. Instead, walk the
/// predecessors and statically evaluate cases based on information available
/// on that edge. Cases that cannot fire no matter what the incoming edge can
/// safely be removed. If a case fires on every incoming edge then the entire
/// switch can be removed and replaced with a branch to the case destination.
bool CorrelatedValuePropagation::processSwitch(SwitchInst *SI) {
Value *Cond = SI->getCondition();
BasicBlock *BB = SI->getParent();

// If the condition was defined in same block as the switch then LazyValueInfo
// currently won't say anything useful about it, though in theory it could.
if (isa<Instruction>(Cond) && cast<Instruction>(Cond)->getParent() == BB)
return false;

// If the switch is unreachable then trying to improve it is a waste of time.
pred_iterator PB = pred_begin(BB), PE = pred_end(BB);
if (PB == PE) return false;

// Analyse each switch case in turn. This is done in reverse order so that
// removing a case doesn't cause trouble for the iteration.
bool Changed = false;
for (SwitchInst::CaseIt CI = SI->caseEnd(), CE = SI->caseBegin(); CI-- != CE;
) {
ConstantInt *Case = CI.getCaseValue();

// Check to see if the switch condition is equal to/not equal to the case
// value on every incoming edge, equal/not equal being the same each time.
LazyValueInfo::Tristate State = LazyValueInfo::Unknown;
for (pred_iterator PI = PB; PI != PE; ++PI) {
// Is the switch condition equal to the case value?
LazyValueInfo::Tristate Value = LVI->getPredicateOnEdge(CmpInst::ICMP_EQ,
Cond, Case, *PI, BB);
// Give up on this case if nothing is known.
if (Value == LazyValueInfo::Unknown) {
State = LazyValueInfo::Unknown;
break;
}

// If this was the first edge to be visited, record that all other edges
// need to give the same result.
if (PI == PB) {
State = Value;
continue;
}

// If this case is known to fire for some edges and known not to fire for
// others then there is nothing we can do - give up.
if (Value != State) {
State = LazyValueInfo::Unknown;
break;
}
}

if (State == LazyValueInfo::False) {
// This case never fires - remove it.
CI.getCaseSuccessor()->removePredecessor(BB);
SI->removeCase(CI); // Does not invalidate the iterator.
Changed = true;
} else if (State == LazyValueInfo::True) {
// This case always fires. Arrange for the switch to be turned into an
// unconditional branch by replacing the switch condition with the case
// value.
SI->setCondition(Case);
Changed = true;
break;
}
}

if (Changed)
// If the switch has been simplified to the point where it can be replaced
// by a branch then do so now.
ConstantFoldTerminator(BB);

return Changed;
}

bool CorrelatedValuePropagation::runOnFunction(Function &F) {
LVI = &getAnalysis<LazyValueInfo>();

Expand Down Expand Up @@ -200,6 +279,13 @@ bool CorrelatedValuePropagation::runOnFunction(Function &F) {
}
}

Instruction *Term = FI->getTerminator();
switch (Term->getOpcode()) {
case Instruction::Switch:
BBChanged |= processSwitch(cast<SwitchInst>(Term));
break;
}

FnChanged |= BBChanged;
}

Expand Down
101 changes: 100 additions & 1 deletion test/Transforms/CorrelatedValuePropagation/basic.ll
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,103 @@ Impossible:

LessThanOrEqualToTwo:
ret i32 0
}
}

define i32 @switch1(i32 %s) {
; CHECK: @switch1
entry:
%cmp = icmp slt i32 %s, 0
br i1 %cmp, label %negative, label %out

negative:
switch i32 %s, label %out [
; CHECK: switch i32 %s, label %out
i32 0, label %out
; CHECK-NOT: i32 0
i32 1, label %out
; CHECK-NOT: i32 1
i32 -1, label %next
; CHECK: i32 -1, label %next
i32 -2, label %next
; CHECK: i32 -2, label %next
i32 2, label %out
; CHECK-NOT: i32 2
i32 3, label %out
; CHECK-NOT: i32 3
]

out:
%p = phi i32 [ 1, %entry ], [ -1, %negative ], [ -1, %negative ], [ -1, %negative ], [ -1, %negative ]
ret i32 %p

next:
%q = phi i32 [ 0, %negative ], [ 0, %negative ]
ret i32 %q
}

define i32 @switch2(i32 %s) {
; CHECK: @switch2
entry:
%cmp = icmp sgt i32 %s, 0
br i1 %cmp, label %positive, label %out

positive:
switch i32 %s, label %out [
i32 0, label %out
i32 -1, label %next
i32 -2, label %next
]
; CHECK: br label %out

out:
%p = phi i32 [ -1, %entry ], [ 1, %positive ], [ 1, %positive ]
ret i32 %p

next:
%q = phi i32 [ 0, %positive ], [ 0, %positive ]
ret i32 %q
}

define i32 @switch3(i32 %s) {
; CHECK: @switch3
entry:
%cmp = icmp sgt i32 %s, 0
br i1 %cmp, label %positive, label %out

positive:
switch i32 %s, label %out [
i32 -1, label %out
i32 -2, label %next
i32 -3, label %next
]
; CHECK: br label %out

out:
%p = phi i32 [ -1, %entry ], [ 1, %positive ], [ 1, %positive ]
ret i32 %p

next:
%q = phi i32 [ 0, %positive ], [ 0, %positive ]
ret i32 %q
}

define void @switch4(i32 %s) {
; CHECK: @switch4
entry:
%cmp = icmp eq i32 %s, 0
br i1 %cmp, label %zero, label %out

zero:
switch i32 %s, label %out [
i32 0, label %next
i32 1, label %out
i32 -1, label %out
]
; CHECK: br label %next

out:
ret void

next:
ret void
}

0 comments on commit 6f1d799

Please sign in to comment.