https://github.com/stephenverderame updated
https://github.com/llvm/llvm-project/pull/115395
>From 71702aca23fcc8e2d104d483a40ae16a4a30e48d Mon Sep 17 00:00:00 2001
From: Stephen Verderame
Date: Mon, 21 Oct 2024 10:01:43 -0700
Subject: [PATCH] [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.
---
clang/lib/CodeGen/CGStmt.cpp | 5 +-
clang/lib/CodeGen/CodeGenFunction.cpp | 110 +-
clang/test/CodeGen/switch-case-overflow.c | 29 ++
3 files changed, 95 insertions(+), 49 deletions(-)
create mode 100644 clang/test/CodeGen/switch-case-overflow.c
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 41dc91c578c800..431ba53d059e7e 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 6ead45793742d6..8b6d7c6b5746c3 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 (!