[clang] [WIP][Clang][CodeGen] Fix crash and generate UnreachableInst upon branching out of SEH handlers (PR #220462)
Ian Li via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 1 19:52:42 PDT 2026
https://github.com/ianayl created https://github.com/llvm/llvm-project/pull/220462
This PR fixes codegen for statements that branch out of SEH handlers causing compiler crashes in LLVM, i.e. see current behavior as of 23.1.0: https://godbolt.org/z/ns3o3h84e
```c
int main() {
__try {
} __finally {
goto illegal_jmp;
}
illegal_jmp:
return 0;
}
```
Instead, this PR tries to adhere to [MSVC C4532](https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-1-c4532) and emits `unreachable` when a statement leads to a `br` out of the immediate SEH handler statement / outlined function. As visible in aforementioned Godbolt link, Sema already contains code emitting warnings on these jumps out of SEH handlers, it's moreso a matter of preventing the compiler crash and adhereing to what's specified in C4532.
>From 3a3863ff81cd51223bf8526c219ae37243db7314 Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Tue, 1 Sep 2026 18:59:02 -0700
Subject: [PATCH] MSVC C4532: Generate "unreachable" if outlined SEH helper has
gotos jumping out of the function
---
clang/lib/CodeGen/CGException.cpp | 7 ++++---
clang/lib/CodeGen/CGStmt.cpp | 28 +++++++++++++++++++++++++++-
clang/lib/CodeGen/CodeGenFunction.h | 13 ++++++++++---
3 files changed, 41 insertions(+), 7 deletions(-)
diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp
index b0fb3b4d85d15..109a02c3b48fe 100644
--- a/clang/lib/CodeGen/CGException.cpp
+++ b/clang/lib/CodeGen/CGException.cpp
@@ -1739,8 +1739,8 @@ struct PerformSEHFinally final : EHScopeStack::Cleanup {
// Compute the two argument values.
QualType ArgTys[2] = {Context.UnsignedCharTy, Context.VoidPtrTy};
llvm::Value *FP = nullptr;
- // If CFG.IsOutlinedSEHHelper is true, then we are within a finally block.
- if (CGF.IsOutlinedSEHHelper) {
+ // If CFG.IsOutlinedSEHHelper() is true, then we are within a finally block.
+ if (CGF.IsOutlinedSEHHelper()) {
FP = &CGF.CurFn->arg_begin()[1];
} else {
llvm::Function *LocalAddrFn =
@@ -2083,7 +2083,8 @@ void CodeGenFunction::startOutlinedSEHHelper(CodeGenFunction &ParentCGF,
llvm::Function *Fn = llvm::Function::Create(
FnTy, llvm::GlobalValue::InternalLinkage, Name.str(), &CGM.getModule());
- IsOutlinedSEHHelper = true;
+ // Indicate the current function is an outlined SEH helper.
+ OutlinedSEHStmt = OutlinedStmt;
StartFunction(GlobalDecl(), RetTy, Fn, FnInfo, Args,
OutlinedStmt->getBeginLoc(), OutlinedStmt->getBeginLoc());
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index bf6e6eb50f555..f979786126c6c 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -848,6 +848,23 @@ void CodeGenFunction::EmitAttributedStmt(const AttributedStmt &S) {
EmitStmt(S.getSubStmt(), S.getAttrs());
}
+static bool StmtContainsLabelDecl(const Stmt *S, const LabelDecl *Target) {
+ if (!S)
+ return false;
+ if (const auto *LS = dyn_cast<LabelStmt>(S))
+ if (LS->getDecl() == Target)
+ return true;
+ for (const Stmt *Child : S->children())
+ if (StmtContainsLabelDecl(Child, Target))
+ return true;
+ return false;
+}
+
+bool CodeGenFunction::IsLabelWithinSEHHelper(const LabelDecl *Label) const {
+ assert(IsOutlinedSEHHelper() && "Not in an outlined SEH helper");
+ return StmtContainsLabelDecl(OutlinedSEHStmt, Label);
+}
+
void CodeGenFunction::EmitGotoStmt(const GotoStmt &S) {
// If this code is reachable then emit a stop point (if generating
// debug info). We have to do this ourselves because we are on the
@@ -855,6 +872,15 @@ void CodeGenFunction::EmitGotoStmt(const GotoStmt &S) {
if (HaveInsertPoint())
EmitStopPoint(&S);
+ // MSVC C4532: jumping out of an outlined SEH helper is UB, but jumping
+ // internally should be fine. Sema has a warn for this already.
+ // `br` should not jump out of the current function anyway, so a jump out of
+ // the outlined SEH helper will cause a compiler crash.
+ if (IsOutlinedSEHHelper() && !IsLabelWithinSEHHelper(S.getLabel())) {
+ Builder.CreateUnreachable();
+ Builder.ClearInsertionPoint();
+ }
+
ApplyAtomGroup Grp(getDebugInfo());
EmitBranchThroughCleanup(getJumpDestForLabel(S.getLabel()));
}
@@ -1627,7 +1653,7 @@ void CodeGenFunction::EmitReturnStmt(const ReturnStmt &S) {
}
// Returning from an outlined SEH helper is UB, and we already warn on it.
- if (IsOutlinedSEHHelper) {
+ if (IsOutlinedSEHHelper()) {
Builder.CreateUnreachable();
Builder.ClearInsertionPoint();
}
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index dfb6f2ff65a7d..9ff301bf4cd60 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -605,9 +605,16 @@ class CodeGenFunction : public CodeGenTypeCache {
GlobalDecl CurSEHParent;
- /// True if the current function is an outlined SEH helper. This can be a
- /// finally block or filter expression.
- bool IsOutlinedSEHHelper = false;
+ /// Pointer to the outlined SEH helper statement if current function is an
+ /// outlined SEH helper, nullptr otherwise. This can be a finally block or
+ /// a filter expression.
+ const Stmt *OutlinedSEHStmt = nullptr;
+
+ /// True if the current function is an outlined SEH helper.
+ bool IsOutlinedSEHHelper() const { return OutlinedSEHStmt != nullptr; }
+
+ /// True if Label is found within OutlinedSEHStmt.
+ bool IsLabelWithinSEHHelper(const LabelDecl *Label) const;
/// True if CodeGen currently emits code inside presereved access index
/// region.
More information about the cfe-commits
mailing list