aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Lattner <sabre@nondot.org>2011-02-28 01:02:29 +0000
committerChris Lattner <sabre@nondot.org>2011-02-28 01:02:29 +0000
commit3858938b043bac2f046304ff99a54905acdcc6dd (patch)
treeb680db64c1a2460d0f7003276d486fc7d4d9d220
parent525544de0835d5ee7497f4897e255727b162e0aa (diff)
make switch condition constant folding much more aggressive, handling
compound statements and break statements. This implements enough to handle PR9322 and rdar://6970405. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@126602 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/CodeGen/CGStmt.cpp69
-rw-r--r--test/CodeGen/switch-dce.c157
2 files changed, 222 insertions, 4 deletions
diff --git a/lib/CodeGen/CGStmt.cpp b/lib/CodeGen/CGStmt.cpp
index 818c568255..70cda5a225 100644
--- a/lib/CodeGen/CGStmt.cpp
+++ b/lib/CodeGen/CGStmt.cpp
@@ -857,6 +857,13 @@ static CSFC_Result CollectStatementsForCase(const Stmt *S,
const SwitchCase *Case,
bool &FoundCase,
llvm::SmallVectorImpl<const Stmt*> &ResultStmts) {
+ assert((!FoundCase || Case == 0) &&
+ "Can't be looking for the case if we already found it!");
+
+ // If this is a null statement, just succeed.
+ if (S == 0)
+ return Case ? CSFC_Success : CSFC_FallThrough;
+
// If this is the switchcase (case 4: or default) that we're looking for, then
// we're in business. Just add the substatement.
if (const SwitchCase *SC = dyn_cast<SwitchCase>(S)) {
@@ -870,12 +877,72 @@ static CSFC_Result CollectStatementsForCase(const Stmt *S,
return CollectStatementsForCase(SC->getSubStmt(), Case, FoundCase,
ResultStmts);
}
+
+ // If we are in the live part of the code and we found our break statement,
+ // return a success!
+ if (Case == 0 && isa<BreakStmt>(S))
+ return CSFC_Success;
+ // If this is a switch statement, then it might contain the SwitchCase, the
+ // break, or neither.
+ if (const CompoundStmt *CS = dyn_cast<CompoundStmt>(S)) {
+ // Handle this as two cases: we might be looking for the SwitchCase (if so
+ // the skipped statements must be skippable) or we might already have it.
+ CompoundStmt::const_body_iterator I = CS->body_begin(), E = CS->body_end();
+ if (Case) {
+ // If we're looking for the case, just see if we can skip each of the
+ // substatements.
+ for (; Case && I != E; ++I) {
+ switch (CollectStatementsForCase(*I, Case, FoundCase, ResultStmts)) {
+ case CSFC_Failure: return CSFC_Failure;
+ case CSFC_Success:
+ // A successful result means that either 1) that the statement doesn't
+ // have the case and is skippable, or 2) does contain the case value
+ // and also contains the break to exit the switch. In either case,
+ // we continue scanning the body of the compound statement to see if
+ // the rest are skippable or have the case.
+ break;
+ case CSFC_FallThrough:
+ // If we have a fallthrough condition, then we must have found the
+ // case started to include statements. Consider the rest of the
+ // statements in the compound statement as candidates for inclusion.
+ assert(FoundCase && "Didn't find case but returned fallthrough?");
+ // We recursively found Case, so we're not looking for it anymore.
+ Case = 0;
+ break;
+ }
+ }
+ }
+
+ // If we have statements in our range, then we know that the statements are
+ // live and need to be added to the set of statements we're tracking.
+ for (; I != E; ++I) {
+ switch (CollectStatementsForCase(*I, 0, FoundCase, ResultStmts)) {
+ case CSFC_Failure: return CSFC_Failure;
+ case CSFC_FallThrough:
+ // A fallthrough result means that the statement was simple and just
+ // included in ResultStmt, keep adding them afterwards.
+ break;
+ case CSFC_Success:
+ // A successful result means that we found the break statement and
+ // stopped statement inclusion. We just ensure that any leftover stmts
+ // are skippable and return success ourselves.
+ for (++I; I != E; ++I)
+ if (CodeGenFunction::ContainsLabel(*I, true))
+ return CSFC_Failure;
+ return CSFC_Success;
+ }
+ }
+
+ return Case ? CSFC_Success : CSFC_FallThrough;
+ }
+
// Okay, this is some other statement that we don't handle explicitly, like a
// for statement or increment etc. If we are skipping over this statement,
// just verify it doesn't have labels, which would make it invalid to elide.
if (Case) {
- if (CodeGenFunction::ContainsLabel(S, true))
+ if (CodeGenFunction::ContainsLabel(S, true) ||
+ isa<DeclStmt>(S))
return CSFC_Failure;
return CSFC_Success;
}
diff --git a/test/CodeGen/switch-dce.c b/test/CodeGen/switch-dce.c
index f06a3ed98c..3276737793 100644
--- a/test/CodeGen/switch-dce.c
+++ b/test/CodeGen/switch-dce.c
@@ -1,18 +1,169 @@
// RUN: %clang_cc1 -triple i386-unknown-unknown -O0 %s -emit-llvm -o - | FileCheck %s
+// PR9322 and rdar://6970405
+
// CHECK: @test1
// CHECK-NOT: switch
+// CHECK-NOT: @dead
// CHECK: add nsw i32 {{.*}}, 1
// CHECK-NOT: switch
-// CHECK-NOT: add nsw i32
+// CHECK-NOT: @dead
// CHECK: ret void
+int i;
+void dead();
+
void test1() {
- int i;
switch (1)
case 1:
++i;
switch (0)
case 1:
- i+=2;
+ dead();
}
+
+
+// CHECK: @test2
+// CHECK-NOT: switch
+// CHECK-NOT: @dead
+// CHECK: add nsw i32 {{.*}}, 2
+// CHECK-NOT: switch
+// CHECK-NOT: @dead
+// CHECK: ret void
+void test2() {
+ switch (4) {
+ case 1:
+ dead();
+ break;
+ case 4:
+ i += 2;
+ // Fall off the end of the switch.
+ }
+}
+
+
+// CHECK: @test3
+// CHECK-NOT: switch
+// CHECK-NOT: @dead
+// CHECK: add nsw i32 {{.*}}, 2
+// CHECK-NOT: switch
+// CHECK-NOT: @dead
+// CHECK: ret void
+void test3() {
+ switch (4) {
+ case 1:
+ dead();
+ break;
+ case 4: {
+ i += 2;
+ break;
+ }
+ }
+}
+
+// CHECK: @test4
+// CHECK-NOT: switch
+// CHECK-NOT: @dead
+// CHECK: add nsw i32 {{.*}}, 2
+// CHECK-NOT: switch
+// CHECK-NOT: @dead
+// CHECK: ret void
+void test4() {
+ switch (4) {
+ case 1:
+ dead();
+ break;
+ default: {
+ i += 2;
+ break;
+ }
+ }
+}
+
+// This shouldn't crash codegen, but we don't have to optimize out the switch
+// in this case.
+void test5() {
+ switch (1) {
+ int x; // eliding var decl?
+ case 1:
+ x = 4;
+ break;
+ }
+}
+
+// CHECK: @test6
+// CHECK-NOT: switch
+// CHECK-NOT: @dead
+// CHECK: ret void
+void test6() {
+ // Neither case is reachable.
+ switch (40) {
+ case 1:
+ dead();
+ break;
+ case 4: {
+ dead();
+ break;
+ }
+ }
+}
+
+// CHECK: @test7
+// CHECK-NOT: switch
+// CHECK-NOT: @dead
+// CHECK: add nsw i32
+// CHECK-NOT: switch
+// CHECK-NOT: @dead
+// CHECK: ret void
+void test7() {
+ switch (4) {
+ case 1:
+ dead();
+ break;
+ {
+ case 4: // crazy brace scenario
+ ++i;
+ }
+ break;
+ }
+}
+
+// CHECK: @test8
+// CHECK-NOT: switch
+// CHECK-NOT: @dead
+// CHECK: add nsw i32
+// CHECK-NOT: switch
+// CHECK-NOT: @dead
+// CHECK: ret void
+void test8() {
+ switch (4) {
+ case 1:
+ dead();
+ break;
+ case 4:
+ ++i;
+ // Fall off the end of the switch.
+ }
+}
+
+// CHECK: @test9
+// CHECK-NOT: switch
+// CHECK-NOT: @dead
+// CHECK: add nsw i32
+// CHECK: add nsw i32
+// CHECK-NOT: switch
+// CHECK-NOT: @dead
+// CHECK: ret void
+void test9(int i) {
+ switch (1) {
+ case 5:
+ dead();
+ case 1:
+ ++i;
+ // Fall through is fine.
+ case 4:
+ ++i;
+ break;
+ }
+}
+