diff options
author | Chris Lattner <sabre@nondot.org> | 2010-12-17 06:20:15 +0000 |
---|---|---|
committer | Chris Lattner <sabre@nondot.org> | 2010-12-17 06:20:15 +0000 |
commit | e27db74a60c44d2b5d2700ecde11b1adce0d0d59 (patch) | |
tree | 656f8aebf6c4dccb96f57e8e42b09bab6bcfe742 | |
parent | 1139d5090ab96e24342f12a6a6817d5898358e1e (diff) |
improve switch formation to handle small range
comparisons formed by comparisons. For example,
this:
void foo(unsigned x) {
if (x == 0 || x == 1 || x == 3 || x == 4 || x == 6)
bar();
}
compiles into:
_foo: ## @foo
## BB#0: ## %entry
cmpl $6, %edi
ja LBB0_2
## BB#1: ## %entry
movl %edi, %eax
movl $91, %ecx
btq %rax, %rcx
jb LBB0_3
instead of:
_foo: ## @foo
## BB#0: ## %entry
cmpl $2, %edi
jb LBB0_4
## BB#1: ## %switch.early.test
cmpl $6, %edi
ja LBB0_3
## BB#2: ## %switch.early.test
movl %edi, %eax
movl $88, %ecx
btq %rax, %rcx
jb LBB0_4
This catches a bunch of cases in GCC, which look like this:
%804 = load i32* @which_alternative, align 4, !tbaa !0
%805 = icmp ult i32 %804, 2
%806 = icmp eq i32 %804, 3
%or.cond121 = or i1 %805, %806
%807 = icmp eq i32 %804, 4
%or.cond124 = or i1 %or.cond121, %807
br i1 %or.cond124, label %.thread, label %808
turning this into a range comparison.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@122045 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/llvm/Support/ConstantRange.h | 2 | ||||
-rw-r--r-- | lib/Transforms/Utils/SimplifyCFG.cpp | 27 | ||||
-rw-r--r-- | test/Transforms/SimplifyCFG/switch_create.ll | 71 |
3 files changed, 96 insertions, 4 deletions
diff --git a/include/llvm/Support/ConstantRange.h b/include/llvm/Support/ConstantRange.h index 96e770951f..ced3a2cf2d 100644 --- a/include/llvm/Support/ConstantRange.h +++ b/include/llvm/Support/ConstantRange.h @@ -54,7 +54,7 @@ public: /// @brief Initialize a range of values explicitly. This will assert out if /// Lower==Upper and Lower != Min or Max value for its type. It will also /// assert out if the two APInt's are not the same bit width. - ConstantRange(const APInt& Lower, const APInt& Upper); + ConstantRange(const APInt &Lower, const APInt &Upper); /// makeICmpRegion - Produce the smallest range that contains all values that /// might satisfy the comparison specified by Pred when compared to any value diff --git a/lib/Transforms/Utils/SimplifyCFG.cpp b/lib/Transforms/Utils/SimplifyCFG.cpp index 23a3a25056..9bb61b6041 100644 --- a/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/lib/Transforms/Utils/SimplifyCFG.cpp @@ -28,6 +28,7 @@ #include "llvm/ADT/Statistic.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/CFG.h" +#include "llvm/Support/ConstantRange.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> @@ -305,11 +306,33 @@ GatherConstantCompares(Value *V, std::vector<ConstantInt*> &Vals, Value *&Extra, // If this is an icmp against a constant, handle this as one of the cases. if (ICmpInst *ICI = dyn_cast<ICmpInst>(I)) { - if (ICI->getPredicate() == (isEQ ? ICmpInst::ICMP_EQ : ICmpInst::ICMP_NE)) - if (ConstantInt *C = GetConstantInt(I->getOperand(1), TD)) { + if (ConstantInt *C = GetConstantInt(I->getOperand(1), TD)) { + if (ICI->getPredicate() == (isEQ ? ICmpInst::ICMP_EQ:ICmpInst::ICMP_NE)) { Vals.push_back(C); return I->getOperand(0); } + + // If we have "x ult 3" comparison, for example, then we can add 0,1,2 to + // the set. + ConstantRange Span = + ConstantRange::makeICmpRegion(ICI->getPredicate(), + ConstantRange(C->getValue())); + + // If this is an and/!= check then we want to optimize "x ugt 2" into + // x != 0 && x != 1. + if (!isEQ) + Span = Span.inverse(); + + // If there are a ton of values, we don't want to make a ginormous switch. + if (Span.getSetSize().getZExtValue() > 8 || Span.isEmptySet() || + // We don't handle wrapped sets yet. + Span.isWrappedSet()) + return 0; + + for (APInt Tmp = Span.getLower(); Tmp != Span.getUpper(); ++Tmp) + Vals.push_back(ConstantInt::get(V->getContext(), Tmp)); + return I->getOperand(0); + } return 0; } diff --git a/test/Transforms/SimplifyCFG/switch_create.ll b/test/Transforms/SimplifyCFG/switch_create.ll index 0169eb7039..369ebc3527 100644 --- a/test/Transforms/SimplifyCFG/switch_create.ll +++ b/test/Transforms/SimplifyCFG/switch_create.ll @@ -351,4 +351,73 @@ malformed: ret void ; CHECK: @test12 -}
\ No newline at end of file +} + +; test13 - handle switch formation with ult. +define void @test13(i32 %x) nounwind ssp noredzone { +entry: + %cmp = icmp ult i32 %x, 2 + br i1 %cmp, label %if.then, label %lor.lhs.false3 + +lor.lhs.false3: ; preds = %lor.lhs.false + %cmp5 = icmp eq i32 %x, 3 + br i1 %cmp5, label %if.then, label %lor.lhs.false6 + +lor.lhs.false6: ; preds = %lor.lhs.false3 + %cmp8 = icmp eq i32 %x, 4 + br i1 %cmp8, label %if.then, label %lor.lhs.false9 + +lor.lhs.false9: ; preds = %lor.lhs.false6 + %cmp11 = icmp eq i32 %x, 6 + br i1 %cmp11, label %if.then, label %if.end + +if.then: ; preds = %lor.lhs.false9, %lor.lhs.false6, %lor.lhs.false3, %lor.lhs.false, %entry + call void @foo1() noredzone + br label %if.end + +if.end: ; preds = %if.then, %lor.lhs.false9 + ret void +; CHECK: @test13 +; CHECK: switch i32 %x, label %if.end [ +; CHECK: i32 6, label %if.then +; CHECK: i32 4, label %if.then +; CHECK: i32 3, label %if.then +; CHECK: i32 1, label %if.then +; CHECK: i32 0, label %if.then +; CHECK: ] +} + +; test14 - handle switch formation with ult. +define void @test14(i32 %x) nounwind ssp noredzone { +entry: + %cmp = icmp ugt i32 %x, 2 + br i1 %cmp, label %lor.lhs.false3, label %if.then + +lor.lhs.false3: ; preds = %lor.lhs.false + %cmp5 = icmp ne i32 %x, 3 + br i1 %cmp5, label %lor.lhs.false6, label %if.then + +lor.lhs.false6: ; preds = %lor.lhs.false3 + %cmp8 = icmp ne i32 %x, 4 + br i1 %cmp8, label %lor.lhs.false9, label %if.then + +lor.lhs.false9: ; preds = %lor.lhs.false6 + %cmp11 = icmp ne i32 %x, 6 + br i1 %cmp11, label %if.end, label %if.then + +if.then: ; preds = %lor.lhs.false9, %lor.lhs.false6, %lor.lhs.false3, %lor.lhs.false, %entry + call void @foo1() noredzone + br label %if.end + +if.end: ; preds = %if.then, %lor.lhs.false9 + ret void +; CHECK: @test14 +; CHECK: switch i32 %x, label %if.end [ +; CHECK: i32 6, label %if.then +; CHECK: i32 4, label %if.then +; CHECK: i32 3, label %if.then +; CHECK: i32 1, label %if.then +; CHECK: i32 0, label %if.then +; CHECK: ] +} + |