diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index 41dc91c578c80..431ba53d059e7 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -1879,7 +1879,7 @@ static CSFC_Result CollectStatementsForCase(const Stmt *S, // 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)) { + while (const SwitchCase *SC = dyn_cast(S)) { if (S == Case) { FoundCase = true; return CollectStatementsForCase(SC->getSubStmt(), nullptr, FoundCase, @@ -1887,8 +1887,7 @@ static CSFC_Result CollectStatementsForCase(const Stmt *S, } // Otherwise, this is some other case or default statement, just ignore it. - return CollectStatementsForCase(SC->getSubStmt(), Case, FoundCase, - ResultStmts); + S = SC->getSubStmt(); } // If we are in the live part of the code and we found our break statement, diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 6ead45793742d..8b6d7c6b5746c 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -1619,31 +1619,38 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn, /// this statement is not executed normally, it not containing a label means /// that we can just remove the code. bool CodeGenFunction::ContainsLabel(const Stmt *S, bool IgnoreCaseStmts) { - // Null statement, not a label! - if (!S) return false; + llvm::SmallVector, 32> WorkList; + WorkList.emplace_back(S, IgnoreCaseStmts); - // If this is a label, we have to emit the code, consider something like: - // if (0) { ... foo: bar(); } goto foo; - // - // TODO: If anyone cared, we could track __label__'s, since we know that you - // can't jump to one from outside their declared region. - if (isa(S)) - return true; + while (!WorkList.empty()) { + auto [CurStmt, CurIgnoreCaseStmts] = WorkList.pop_back_val(); - // If this is a case/default statement, and we haven't seen a switch, we have - // to emit the code. - if (isa(S) && !IgnoreCaseStmts) - return true; + // Null statement, not a label! + if (!CurStmt) + continue; - // If this is a switch statement, we want to ignore cases below it. - if (isa(S)) - IgnoreCaseStmts = true; + // If this is a label, we have to emit the code, consider something like: + // if (0) { ... foo: bar(); } goto foo; + // + // TODO: If anyone cared, we could track __label__'s, since we know that you + // can't jump to one from outside their declared region. + if (isa(CurStmt)) + return true; - // Scan subexpressions for verboten labels. - for (const Stmt *SubStmt : S->children()) - if (ContainsLabel(SubStmt, IgnoreCaseStmts)) + // If this is a case/default statement, and we haven't seen a switch, we + // have to emit the code. + if (isa(CurStmt) && !CurIgnoreCaseStmts) return true; + // If this is a switch statement, we want to ignore cases below it. + if (isa(CurStmt)) + CurIgnoreCaseStmts = true; + + // Scan subexpressions for verboten labels. + for (const Stmt *SubStmt : CurStmt->children()) + WorkList.emplace_back(SubStmt, CurIgnoreCaseStmts); + } + return false; } @@ -1651,46 +1658,57 @@ bool CodeGenFunction::ContainsLabel(const Stmt *S, bool IgnoreCaseStmts) { /// If the statement (recursively) contains a switch or loop with a break /// inside of it, this is fine. bool CodeGenFunction::containsBreak(const Stmt *S) { - // Null statement, not a label! - if (!S) return false; + llvm::SmallVector WorkList; + WorkList.push_back(S); + while (!WorkList.empty()) { + const Stmt *CurStmt = WorkList.pop_back_val(); - // If this is a switch or loop that defines its own break scope, then we can - // include it and anything inside of it. - if (isa(S) || isa(S) || isa(S) || - isa(S)) - return false; + // Null statement, not a label! + if (!CurStmt) + continue; - if (isa(S)) - return true; + // If this is a switch or loop that defines its own break scope, then we can + // include it and anything inside of it. + if (isa(CurStmt) || isa(CurStmt) || + isa(CurStmt) || isa(CurStmt)) + continue; - // Scan subexpressions for verboten breaks. - for (const Stmt *SubStmt : S->children()) - if (containsBreak(SubStmt)) + if (isa(CurStmt)) return true; + // Scan subexpressions for verboten breaks. + for (const Stmt *SubStmt : CurStmt->children()) + WorkList.push_back(SubStmt); + } return false; } bool CodeGenFunction::mightAddDeclToScope(const Stmt *S) { - if (!S) return false; - - // Some statement kinds add a scope and thus never add a decl to the current - // scope. Note, this list is longer than the list of statements that might - // have an unscoped decl nested within them, but this way is conservatively - // correct even if more statement kinds are added. - if (isa(S) || isa(S) || isa(S) || - isa(S) || isa(S) || isa(S) || - isa(S) || isa(S) || - isa(S) || isa(S)) - return false; + llvm::SmallVector WorkList; + WorkList.push_back(S); + while (!WorkList.empty()) { + const Stmt *CurStmt = WorkList.pop_back_val(); - if (isa(S)) - return true; + if (!CurStmt) + continue; - for (const Stmt *SubStmt : S->children()) - if (mightAddDeclToScope(SubStmt)) + // Some statement kinds add a scope and thus never add a decl to the current + // scope. Note, this list is longer than the list of statements that might + // have an unscoped decl nested within them, but this way is conservatively + // correct even if more statement kinds are added. + if (isa(CurStmt) || isa(CurStmt) || + isa(CurStmt) || isa(CurStmt) || + isa(CurStmt) || isa(CurStmt) || + isa(CurStmt) || isa(CurStmt) || + isa(CurStmt) || isa(CurStmt)) + continue; + + if (isa(CurStmt)) return true; + for (const Stmt *SubStmt : CurStmt->children()) + WorkList.push_back(SubStmt); + } return false; } diff --git a/clang/test/CodeGen/switch-case-overflow.c b/clang/test/CodeGen/switch-case-overflow.c new file mode 100644 index 0000000000000..f548d645e322b --- /dev/null +++ b/clang/test/CodeGen/switch-case-overflow.c @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck %s + +#define CASES(n) case (n): case (n + 1): case (n + 2): case (n + 3): +#define CASES16(n) CASES(n) CASES(n + 4) CASES(n + 8) CASES(n + 12) +#define CASES64(n) CASES16(n) CASES16(n + 16) CASES16(n + 32) CASES16(n + 48) +#define CASES256(n) CASES64(n) CASES64(n + 64) CASES64(n + 128) CASES64(n + 192) +#define CASES1024(n) CASES256(n) CASES256(n + 256) CASES256(n + 512) CASES256(n + 768) +#define CASES4192(n) CASES1024(n) CASES1024(n + 1024) CASES1024(n + 2048) CASES1024(n + 3072) +#define CASES16768(n) CASES4192(n) CASES4192(n + 4192) CASES4192(n + 8384) CASES4192(n + 12576) +#define CASES_STARTING_AT(n) CASES16768(n) CASES16768(n + 16768) CASES16768(n + 33536) CASES16768(n + 50304) + +// Check this doesn't cause the compiler to crash +void foo() { + // CHECK-LABEL: @foo + // CHECK-NOT: switch{{ }} + // CHECK-NOT: br{{ }} + + // 37 does not match a switch case + switch (37) { + CASES_STARTING_AT(100) + break; + } + + // 2000 matches a switch case + switch(2000) { + CASES_STARTING_AT(0) + break; + } +}