From: Chris Lattner Date: Mon, 28 Feb 2011 01:02:29 +0000 (+0000) Subject: make switch condition constant folding much more aggressive, handling X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3858938b043bac2f046304ff99a54905acdcc6dd;p=clang 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 --- 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 &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(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(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(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(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; + } +} +