Skip to content

Commit 71702ac

Browse files
[clang] Use worklist for some CodeGenFunctions
This converts some CodeGenFunctions that are called when emitting switch statements to be iterative. Recursive implementations of functions used to determine when certain cases can be omitted can cause a stack overflow when attempting to generate IR for deeply nested branches or switch cases.
1 parent 21ded66 commit 71702ac

File tree

3 files changed

+95
-49
lines changed

3 files changed

+95
-49
lines changed

clang/lib/CodeGen/CGStmt.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1879,16 +1879,15 @@ static CSFC_Result CollectStatementsForCase(const Stmt *S,
18791879

18801880
// If this is the switchcase (case 4: or default) that we're looking for, then
18811881
// we're in business. Just add the substatement.
1882-
if (const SwitchCase *SC = dyn_cast<SwitchCase>(S)) {
1882+
while (const SwitchCase *SC = dyn_cast<SwitchCase>(S)) {
18831883
if (S == Case) {
18841884
FoundCase = true;
18851885
return CollectStatementsForCase(SC->getSubStmt(), nullptr, FoundCase,
18861886
ResultStmts);
18871887
}
18881888

18891889
// Otherwise, this is some other case or default statement, just ignore it.
1890-
return CollectStatementsForCase(SC->getSubStmt(), Case, FoundCase,
1891-
ResultStmts);
1890+
S = SC->getSubStmt();
18921891
}
18931892

18941893
// If we are in the live part of the code and we found our break statement,

clang/lib/CodeGen/CodeGenFunction.cpp

Lines changed: 64 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1619,78 +1619,96 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
16191619
/// this statement is not executed normally, it not containing a label means
16201620
/// that we can just remove the code.
16211621
bool CodeGenFunction::ContainsLabel(const Stmt *S, bool IgnoreCaseStmts) {
1622-
// Null statement, not a label!
1623-
if (!S) return false;
1622+
llvm::SmallVector<std::pair<const Stmt *, bool>, 32> WorkList;
1623+
WorkList.emplace_back(S, IgnoreCaseStmts);
16241624

1625-
// If this is a label, we have to emit the code, consider something like:
1626-
// if (0) { ... foo: bar(); } goto foo;
1627-
//
1628-
// TODO: If anyone cared, we could track __label__'s, since we know that you
1629-
// can't jump to one from outside their declared region.
1630-
if (isa<LabelStmt>(S))
1631-
return true;
1625+
while (!WorkList.empty()) {
1626+
auto [CurStmt, CurIgnoreCaseStmts] = WorkList.pop_back_val();
16321627

1633-
// If this is a case/default statement, and we haven't seen a switch, we have
1634-
// to emit the code.
1635-
if (isa<SwitchCase>(S) && !IgnoreCaseStmts)
1636-
return true;
1628+
// Null statement, not a label!
1629+
if (!CurStmt)
1630+
continue;
16371631

1638-
// If this is a switch statement, we want to ignore cases below it.
1639-
if (isa<SwitchStmt>(S))
1640-
IgnoreCaseStmts = true;
1632+
// If this is a label, we have to emit the code, consider something like:
1633+
// if (0) { ... foo: bar(); } goto foo;
1634+
//
1635+
// TODO: If anyone cared, we could track __label__'s, since we know that you
1636+
// can't jump to one from outside their declared region.
1637+
if (isa<LabelStmt>(CurStmt))
1638+
return true;
16411639

1642-
// Scan subexpressions for verboten labels.
1643-
for (const Stmt *SubStmt : S->children())
1644-
if (ContainsLabel(SubStmt, IgnoreCaseStmts))
1640+
// If this is a case/default statement, and we haven't seen a switch, we
1641+
// have to emit the code.
1642+
if (isa<SwitchCase>(CurStmt) && !CurIgnoreCaseStmts)
16451643
return true;
16461644

1645+
// If this is a switch statement, we want to ignore cases below it.
1646+
if (isa<SwitchStmt>(CurStmt))
1647+
CurIgnoreCaseStmts = true;
1648+
1649+
// Scan subexpressions for verboten labels.
1650+
for (const Stmt *SubStmt : CurStmt->children())
1651+
WorkList.emplace_back(SubStmt, CurIgnoreCaseStmts);
1652+
}
1653+
16471654
return false;
16481655
}
16491656

16501657
/// containsBreak - Return true if the statement contains a break out of it.
16511658
/// If the statement (recursively) contains a switch or loop with a break
16521659
/// inside of it, this is fine.
16531660
bool CodeGenFunction::containsBreak(const Stmt *S) {
1654-
// Null statement, not a label!
1655-
if (!S) return false;
1661+
llvm::SmallVector<const Stmt *, 32> WorkList;
1662+
WorkList.push_back(S);
1663+
while (!WorkList.empty()) {
1664+
const Stmt *CurStmt = WorkList.pop_back_val();
16561665

1657-
// If this is a switch or loop that defines its own break scope, then we can
1658-
// include it and anything inside of it.
1659-
if (isa<SwitchStmt>(S) || isa<WhileStmt>(S) || isa<DoStmt>(S) ||
1660-
isa<ForStmt>(S))
1661-
return false;
1666+
// Null statement, not a label!
1667+
if (!CurStmt)
1668+
continue;
16621669

1663-
if (isa<BreakStmt>(S))
1664-
return true;
1670+
// If this is a switch or loop that defines its own break scope, then we can
1671+
// include it and anything inside of it.
1672+
if (isa<SwitchStmt>(CurStmt) || isa<WhileStmt>(CurStmt) ||
1673+
isa<DoStmt>(CurStmt) || isa<ForStmt>(CurStmt))
1674+
continue;
16651675

1666-
// Scan subexpressions for verboten breaks.
1667-
for (const Stmt *SubStmt : S->children())
1668-
if (containsBreak(SubStmt))
1676+
if (isa<BreakStmt>(CurStmt))
16691677
return true;
16701678

1679+
// Scan subexpressions for verboten breaks.
1680+
for (const Stmt *SubStmt : CurStmt->children())
1681+
WorkList.push_back(SubStmt);
1682+
}
16711683
return false;
16721684
}
16731685

16741686
bool CodeGenFunction::mightAddDeclToScope(const Stmt *S) {
1675-
if (!S) return false;
1676-
1677-
// Some statement kinds add a scope and thus never add a decl to the current
1678-
// scope. Note, this list is longer than the list of statements that might
1679-
// have an unscoped decl nested within them, but this way is conservatively
1680-
// correct even if more statement kinds are added.
1681-
if (isa<IfStmt>(S) || isa<SwitchStmt>(S) || isa<WhileStmt>(S) ||
1682-
isa<DoStmt>(S) || isa<ForStmt>(S) || isa<CompoundStmt>(S) ||
1683-
isa<CXXForRangeStmt>(S) || isa<CXXTryStmt>(S) ||
1684-
isa<ObjCForCollectionStmt>(S) || isa<ObjCAtTryStmt>(S))
1685-
return false;
1687+
llvm::SmallVector<const Stmt *, 32> WorkList;
1688+
WorkList.push_back(S);
1689+
while (!WorkList.empty()) {
1690+
const Stmt *CurStmt = WorkList.pop_back_val();
16861691

1687-
if (isa<DeclStmt>(S))
1688-
return true;
1692+
if (!CurStmt)
1693+
continue;
16891694

1690-
for (const Stmt *SubStmt : S->children())
1691-
if (mightAddDeclToScope(SubStmt))
1695+
// Some statement kinds add a scope and thus never add a decl to the current
1696+
// scope. Note, this list is longer than the list of statements that might
1697+
// have an unscoped decl nested within them, but this way is conservatively
1698+
// correct even if more statement kinds are added.
1699+
if (isa<IfStmt>(CurStmt) || isa<SwitchStmt>(CurStmt) ||
1700+
isa<WhileStmt>(CurStmt) || isa<DoStmt>(CurStmt) ||
1701+
isa<ForStmt>(CurStmt) || isa<CompoundStmt>(CurStmt) ||
1702+
isa<CXXForRangeStmt>(CurStmt) || isa<CXXTryStmt>(CurStmt) ||
1703+
isa<ObjCForCollectionStmt>(CurStmt) || isa<ObjCAtTryStmt>(CurStmt))
1704+
continue;
1705+
1706+
if (isa<DeclStmt>(CurStmt))
16921707
return true;
16931708

1709+
for (const Stmt *SubStmt : CurStmt->children())
1710+
WorkList.push_back(SubStmt);
1711+
}
16941712
return false;
16951713
}
16961714

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck %s
2+
3+
#define CASES(n) case (n): case (n + 1): case (n + 2): case (n + 3):
4+
#define CASES16(n) CASES(n) CASES(n + 4) CASES(n + 8) CASES(n + 12)
5+
#define CASES64(n) CASES16(n) CASES16(n + 16) CASES16(n + 32) CASES16(n + 48)
6+
#define CASES256(n) CASES64(n) CASES64(n + 64) CASES64(n + 128) CASES64(n + 192)
7+
#define CASES1024(n) CASES256(n) CASES256(n + 256) CASES256(n + 512) CASES256(n + 768)
8+
#define CASES4192(n) CASES1024(n) CASES1024(n + 1024) CASES1024(n + 2048) CASES1024(n + 3072)
9+
#define CASES16768(n) CASES4192(n) CASES4192(n + 4192) CASES4192(n + 8384) CASES4192(n + 12576)
10+
#define CASES_STARTING_AT(n) CASES16768(n) CASES16768(n + 16768) CASES16768(n + 33536) CASES16768(n + 50304)
11+
12+
// Check this doesn't cause the compiler to crash
13+
void foo() {
14+
// CHECK-LABEL: @foo
15+
// CHECK-NOT: switch{{ }}
16+
// CHECK-NOT: br{{ }}
17+
18+
// 37 does not match a switch case
19+
switch (37) {
20+
CASES_STARTING_AT(100)
21+
break;
22+
}
23+
24+
// 2000 matches a switch case
25+
switch(2000) {
26+
CASES_STARTING_AT(0)
27+
break;
28+
}
29+
}

0 commit comments

Comments
 (0)