[llvm] [WinEH] Emit unreachable on malformed catchpad (PR #222181)
Aaron Danen via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 9 14:07:31 PDT 2026
https://github.com/aadanen updated https://github.com/llvm/llvm-project/pull/222181
>From 5465f0b6d5d277769a549c62f4600ee16ab636cd Mon Sep 17 00:00:00 2001
From: Aaron Danen <aaron.danen at gmail.com>
Date: Tue, 8 Sep 2026 14:08:19 -0700
Subject: [PATCH 1/2] [WinEH] Emit unreachable on malformed catchpad
format
---
llvm/lib/CodeGen/WinEHPrepare.cpp | 149 +++++++++++--
llvm/test/CodeGen/WinEH/wineh-cloning.ll | 20 +-
llvm/test/CodeGen/WinEH/wineh-demotion.ll | 18 +-
.../CodeGen/WinEH/wineh-malformed-catchpad.ll | 203 ++++++++++++++++++
4 files changed, 349 insertions(+), 41 deletions(-)
create mode 100644 llvm/test/CodeGen/WinEH/wineh-malformed-catchpad.ll
diff --git a/llvm/lib/CodeGen/WinEHPrepare.cpp b/llvm/lib/CodeGen/WinEHPrepare.cpp
index 40cb11cbeb0de..272291dd2f05c 100644
--- a/llvm/lib/CodeGen/WinEHPrepare.cpp
+++ b/llvm/lib/CodeGen/WinEHPrepare.cpp
@@ -19,6 +19,7 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/WinEHFuncInfo.h"
@@ -58,6 +59,65 @@ static cl::opt<bool> DemoteCatchSwitchPHIOnlyOpt(
"demote-catchswitch-only", cl::Hidden,
cl::desc("Demote catchswitch BBs only (for wasm EH)"), cl::init(false));
+// assumes Personality is one relevant to WinEHPrepare
+static bool isMalformedCatchpad(const CatchPadInst *CPI,
+ EHPersonality Personality) {
+ switch (Personality) {
+ case EHPersonality::MSVC_CXX: {
+ if (CPI->arg_size() != 3)
+ return true;
+
+ Constant *TypeInfo;
+ GlobalVariable *TypeDescriptor;
+ ConstantInt *Adjectives;
+
+ if (!(TypeInfo = dyn_cast<Constant>(CPI->getArgOperand(0))))
+ return true;
+ if (TypeInfo->isNullValue())
+ return false;
+
+ if (!(TypeDescriptor =
+ dyn_cast<GlobalVariable>(TypeInfo->stripPointerCasts())))
+ return true;
+
+ if (!(Adjectives = dyn_cast<ConstantInt>(CPI->getArgOperand(1))))
+ return true;
+
+ return false;
+ }
+ case EHPersonality::MSVC_X86SEH:
+ case EHPersonality::MSVC_TableSEH: {
+ // Argument 0 is the filter function, or null for a catch-all; only it
+ // is ever consulted, so extra trailing arguments are tolerated.
+ if (CPI->arg_size() == 0)
+ return true;
+
+ Constant *FilterOrNull;
+ if (!(FilterOrNull = dyn_cast<Constant>(CPI->getArgOperand(0))))
+ return true;
+
+ Constant *Filter = FilterOrNull->stripPointerCasts();
+ if (!isa<Function>(Filter) && !Filter->isNullValue())
+ return true;
+
+ return false;
+ }
+ case EHPersonality::CoreCLR: {
+ // Argument 0 is the integer type token; only it is ever consulted, so
+ // extra trailing arguments are tolerated.
+ if (CPI->arg_size() == 0)
+ return true;
+
+ if (!dyn_cast<ConstantInt>(CPI->getArgOperand(0)))
+ return true;
+
+ return false;
+ }
+ default:
+ return false;
+ }
+}
+
namespace {
class WinEHPrepareImpl {
@@ -80,6 +140,7 @@ class WinEHPrepareImpl {
bool demotePHIsOnFunclets(Function &F, bool DemoteCatchSwitchPHIOnly);
bool cloneCommonBlocks(Function &F);
+ bool removeMalformedCatchswitch(Function &F);
bool removeImplausibleInstructions(Function &F);
bool cleanupPreparedFunclets(Function &F);
void verifyPreparedFunclets(Function &F);
@@ -162,18 +223,19 @@ static void addTryBlockMapEntry(WinEHFuncInfo &FuncInfo, int TryLow,
assert(TBME.TryLow <= TBME.TryHigh);
for (const CatchPadInst *CPI : Handlers) {
WinEHHandlerType HT;
- Constant *TypeInfo = cast<Constant>(CPI->getArgOperand(0));
- if (TypeInfo->isNullValue())
- HT.TypeDescriptor = nullptr;
- else
- HT.TypeDescriptor = cast<GlobalVariable>(TypeInfo->stripPointerCasts());
- HT.Adjectives = cast<ConstantInt>(CPI->getArgOperand(1))->getZExtValue();
- HT.Handler = CPI->getParent();
- if (auto *AI =
- dyn_cast<AllocaInst>(CPI->getArgOperand(2)->stripPointerCasts()))
- HT.CatchObj.Alloca = AI;
- else
- HT.CatchObj.Alloca = nullptr;
+ HT.TypeDescriptor = nullptr;
+ HT.Adjectives = 0;
+ HT.CatchObj.Alloca = nullptr;
+ if (!isMalformedCatchpad(CPI, EHPersonality::MSVC_CXX)) {
+ Constant *TypeInfo = cast<Constant>(CPI->getArgOperand(0));
+ if (!TypeInfo->isNullValue())
+ HT.TypeDescriptor = cast<GlobalVariable>(TypeInfo->stripPointerCasts());
+ HT.Adjectives = cast<ConstantInt>(CPI->getArgOperand(1))->getZExtValue();
+ HT.Handler = CPI->getParent();
+ if (auto *AI =
+ dyn_cast<AllocaInst>(CPI->getArgOperand(2)->stripPointerCasts()))
+ HT.CatchObj.Alloca = AI;
+ }
TBME.HandlerArray.push_back(HT);
}
FuncInfo.TryBlockMap.push_back(TBME);
@@ -324,9 +386,13 @@ void llvm::calculateSEHStateForAsynchEH(const BasicBlock *BB, int State,
EHInfo.BlockToStateMap[BB] = State; // Record state
if (isa<CatchPadInst>(It) && isa<CatchReturnInst>(TI)) {
- const Constant *FilterOrNull = cast<Constant>(
- cast<CatchPadInst>(It)->getArgOperand(0)->stripPointerCasts());
- const Function *Filter = dyn_cast<Function>(FilterOrNull);
+ const auto *CPI = cast<CatchPadInst>(It);
+ const Function *Filter = nullptr;
+ if (!isMalformedCatchpad(CPI, EHPersonality::MSVC_X86SEH)) {
+ const Constant *FilterOrNull =
+ cast<Constant>(CPI->getArgOperand(0)->stripPointerCasts());
+ Filter = dyn_cast<Function>(FilterOrNull);
+ }
if (!Filter || !Filter->getName().starts_with("__IsLocalUnwind"))
State = EHInfo.SEHUnwindMap[State].ToState; // Retrive next State
} else if ((isa<CleanupReturnInst>(TI) || isa<CatchReturnInst>(TI)) &&
@@ -514,11 +580,14 @@ static void calculateSEHStateNumbers(WinEHFuncInfo &FuncInfo,
const auto *CatchPad =
cast<CatchPadInst>((*CatchSwitch->handler_begin())->getFirstNonPHIIt());
const BasicBlock *CatchPadBB = CatchPad->getParent();
- const Constant *FilterOrNull =
- cast<Constant>(CatchPad->getArgOperand(0)->stripPointerCasts());
- const Function *Filter = dyn_cast<Function>(FilterOrNull);
- assert((Filter || FilterOrNull->isNullValue()) &&
- "unexpected filter value");
+ const Function *Filter = nullptr;
+ if (!isMalformedCatchpad(CatchPad, EHPersonality::MSVC_X86SEH)) {
+ const Constant *FilterOrNull =
+ cast<Constant>(CatchPad->getArgOperand(0)->stripPointerCasts());
+ Filter = dyn_cast<Function>(FilterOrNull);
+ assert((Filter || FilterOrNull->isNullValue()) &&
+ "unexpected filter value");
+ }
int TryState = addSEHExcept(FuncInfo, ParentState, Filter, CatchPadBB);
// Everything in the __try block uses TryState as its parent state.
@@ -730,8 +799,10 @@ void llvm::calculateClrEHStateNumbers(const Function *Fn,
// Create the entry for this catch with the appropriate handler
// properties.
const auto *Catch = cast<CatchPadInst>(CatchBlock->getFirstNonPHIIt());
- uint32_t TypeToken = static_cast<uint32_t>(
- cast<ConstantInt>(Catch->getArgOperand(0))->getZExtValue());
+ uint32_t TypeToken = 0;
+ if (!isMalformedCatchpad(Catch, EHPersonality::CoreCLR))
+ TypeToken = static_cast<uint32_t>(
+ cast<ConstantInt>(Catch->getArgOperand(0))->getZExtValue());
CatchState =
addClrEHHandler(FuncInfo, HandlerParentState, FollowerState,
ClrHandlerType::Catch, TypeToken, CatchBlock);
@@ -1118,6 +1189,37 @@ bool WinEHPrepareImpl::cloneCommonBlocks(Function &F) {
return Changed;
}
+bool WinEHPrepareImpl::removeMalformedCatchswitch(Function &F) {
+ bool Changed = false;
+
+ // If a catchpad is malformed, the whole catchswitch is invalidated
+ // therefore, make all child catchpads unreachable
+ SmallPtrSet<CatchSwitchInst *, 4> Invalidated;
+ for (auto &Funclet : FuncletBlocks) {
+ BasicBlock *FuncletPadBB = Funclet.first;
+ Instruction *FirstNonPHI = &*FuncletPadBB->getFirstNonPHIIt();
+ auto *FuncletPad = dyn_cast<FuncletPadInst>(FirstNonPHI);
+ auto *CatchPad = dyn_cast_or_null<CatchPadInst>(FuncletPad);
+
+ if (!CatchPad)
+ continue;
+
+ if (!isMalformedCatchpad(CatchPad, Personality))
+ continue;
+
+ CatchSwitchInst *CatchSwitch = CatchPad->getCatchSwitch();
+ if (!Invalidated.insert(CatchSwitch).second)
+ continue;
+
+ for (BasicBlock *Handler : CatchSwitch->handlers())
+ changeToUnreachable(Handler->getFirstNonPHIIt()->getNextNode());
+
+ Changed = true;
+ }
+
+ return Changed;
+}
+
bool WinEHPrepareImpl::removeImplausibleInstructions(Function &F) {
bool Changed = false;
@@ -1249,6 +1351,9 @@ bool WinEHPrepareImpl::prepareExplicitEH(Function &F) {
DemoteCatchSwitchPHIOnlyOpt);
if (!DisableCleanups) {
+ assert(!verifyFunction(F, &dbgs()));
+ Changed |= removeMalformedCatchswitch(F);
+
assert(!verifyFunction(F, &dbgs()));
Changed |= removeImplausibleInstructions(F);
diff --git a/llvm/test/CodeGen/WinEH/wineh-cloning.ll b/llvm/test/CodeGen/WinEH/wineh-cloning.ll
index 2b9e3bd06407b..889009939dff8 100644
--- a/llvm/test/CodeGen/WinEH/wineh-cloning.ll
+++ b/llvm/test/CodeGen/WinEH/wineh-cloning.ll
@@ -23,7 +23,7 @@ entry:
catch.switch:
%cs = catchswitch within none [label %catch] unwind to caller
catch:
- %cp = catchpad within %cs []
+ %cp = catchpad within %cs [ptr null, i32 0, ptr null]
br label %noreturn
noreturn:
; %x use colors: {entry, cleanup}
@@ -41,7 +41,7 @@ noreturn:
; CHECK: catch.switch:
; CHECK: %cs = catchswitch within none [label %catch] unwind to caller
; CHECK: catch:
-; CHECK: catchpad within %cs []
+; CHECK: catchpad within %cs [ptr null, i32 0, ptr null]
; CHECK-NEXT: call void @llvm.foo(i32 %x)
; CHECK: [[EntryCopy]]:
; CHECK: call void @llvm.foo(i32 %x)
@@ -84,7 +84,7 @@ invoke.cont:
catch.switch:
%cs = catchswitch within none [label %catch] unwind to caller
catch:
- catchpad within %cs []
+ catchpad within %cs [ptr null, i32 0, ptr null]
br label %shared
cleanup:
cleanuppad within none []
@@ -102,7 +102,7 @@ exit:
; CHECK: invoke void @f()
; CHECK: to label %[[exit:[^ ]+]] unwind
; CHECK: catch:
-; CHECK: catchpad within %cs []
+; CHECK: catchpad within %cs [ptr null, i32 0, ptr null]
; CHECK-NEXT: call void @llvm.bar()
; CHECK-NEXT: unreachable
; CHECK: cleanup:
@@ -120,7 +120,7 @@ entry:
catch.switch:
%cs = catchswitch within none [label %catch] unwind to caller
catch:
- catchpad within %cs []
+ catchpad within %cs [ptr null, i32 0, ptr null]
br label %shared
shared:
%x = call i32 @llvm.qux()
@@ -152,7 +152,7 @@ exit:
; CHECK: entry:
; CHECK: to label %[[shared_E:[^ ]+]] unwind label %catch.switch
; CHECK: catch:
-; CHECK: catchpad within %cs []
+; CHECK: catchpad within %cs [ptr null, i32 0, ptr null]
; CHECK: [[x_C:%[^ ]+]] = call i32 @llvm.qux()
; CHECK: [[i_C:%[^ ]+]] = call i32 @llvm.qux()
; CHECK: [[zt_C:%[^ ]+]] = icmp eq i32 [[i_C]], 0
@@ -210,7 +210,7 @@ outer:
catch.switch:
%cs = catchswitch within %o [label %inner] unwind to caller
inner:
- %i = catchpad within %cs []
+ %i = catchpad within %cs [ptr null]
catchret from %i to label %outer.post-inner
outer.post-inner:
call void @llvm.foo(i32 %x)
@@ -229,7 +229,7 @@ exit:
; CHECK-NEXT: invoke void @f()
; CHECK-NEXT: to label %outer.ret unwind label %catch.switch
; CHECK: inner:
-; CHECK-NEXT: %i = catchpad within %cs []
+; CHECK-NEXT: %i = catchpad within %cs [ptr null]
; CHECK-NEXT: catchret from %i to label %outer.post-inner
; CHECK: outer.post-inner:
; CHECK-NEXT: call void @llvm.foo(i32 %x)
@@ -249,7 +249,7 @@ outer:
%cs = catchswitch within none [label %catch.body] unwind to caller
catch.body:
- %catch = catchpad within %cs []
+ %catch = catchpad within %cs [ptr null, i32 0, ptr null]
catchret from %catch to label %exit
exit:
ret void
@@ -266,7 +266,7 @@ unreachable:
; CHECK: outer:
; CHECK-NEXT: %cs = catchswitch within none [label %catch.body] unwind to caller
; CHECK: catch.body:
-; CHECK-NEXT: %catch = catchpad within %cs []
+; CHECK-NEXT: %catch = catchpad within %cs [ptr null, i32 0, ptr null]
; CHECK-NEXT: catchret from %catch to label %exit
; CHECK: exit:
; CHECK-NEXT: ret void
diff --git a/llvm/test/CodeGen/WinEH/wineh-demotion.ll b/llvm/test/CodeGen/WinEH/wineh-demotion.ll
index 4e44c081ef586..39ac51adeef23 100644
--- a/llvm/test/CodeGen/WinEH/wineh-demotion.ll
+++ b/llvm/test/CodeGen/WinEH/wineh-demotion.ll
@@ -44,7 +44,7 @@ merge:
%cs1 = catchswitch within none [label %catch] unwind to caller
catch:
- %cp = catchpad within %cs1 []
+ %cp = catchpad within %cs1 [ptr null, i32 0, ptr null]
; CHECK: catch:
; CHECK: [[Reload:%[^ ]+]] = load i32, ptr [[Slot]]
; CHECK-NEXT: call void @h(i32 [[Reload]])
@@ -83,7 +83,7 @@ merge.inner:
%cs1 = catchswitch within none [label %catch.inner] unwind label %merge.outer
catch.inner:
- %cpinner = catchpad within %cs1 []
+ %cpinner = catchpad within %cs1 [ptr null, i32 0, ptr null]
; Need just one store here because only %y is affected
; CHECK: catch.inner:
%z = call i32 @g() [ "funclet"(token %cpinner) ]
@@ -103,9 +103,9 @@ merge.outer:
%cs2 = catchswitch within none [label %catch.outer] unwind to caller
catch.outer:
- %cpouter = catchpad within %cs2 []
+ %cpouter = catchpad within %cs2 [ptr null, i32 0, ptr null]
; CHECK: catch.outer:
- ; CHECK: [[CatchPad:%[^ ]+]] = catchpad within %cs2 []
+ ; CHECK: [[CatchPad:%[^ ]+]] = catchpad within %cs2 [ptr null, i32 0, ptr null]
; Need to load x and y from two different slots since they're both live
; and can have different values (if we came from catch.inner)
; CHECK-DAG: load i32, ptr [[Slot1]]
@@ -150,7 +150,7 @@ catchpad.inner:
%phi.inner = phi i32 [ %l, %left ], [ %r, %right ]
%cs1 = catchswitch within none [label %catch.inner] unwind label %catchpad.outer
catch.inner:
- %cp1 = catchpad within %cs1 []
+ %cp1 = catchpad within %cs1 [ptr null, i32 0, ptr null]
catchret from %cp1 to label %join
join:
; CHECK: join:
@@ -170,7 +170,7 @@ catch.outer:
; CHECK: catch.outer:
; CHECK: [[Reload:%[^ ]+]] = load i32, ptr [[Slot]]
; CHECK: call void @h(i32 [[Reload]])
- %cp2 = catchpad within %cs2 []
+ %cp2 = catchpad within %cs2 [ptr null, i32 0, ptr null]
call void @h(i32 %phi.outer) [ "funclet"(token %cp2) ]
catchret from %cp2 to label %exit
exit:
@@ -243,7 +243,7 @@ catch:
; CHECK: catchpad within %cs1
; CHECK: [[CatchReload:%[^ ]+]] = load i32, ptr [[CatchSlot]]
; CHECK: call void @h(i32 [[CatchReload]]
- %cp2 = catchpad within %cs1 []
+ %cp2 = catchpad within %cs1 [ptr null, i32 0, ptr null]
call void @h(i32 %phi.catch) [ "funclet"(token %cp2) ]
catchret from %cp2 to label %exit
@@ -298,8 +298,8 @@ catchpad:
%cs1 = catchswitch within none [label %catch] unwind to caller
catch:
; CHECK: catch:
- ; CHECK-NEXT: %[[CatchPad:[^ ]+]] = catchpad within %cs1 []
- %cp = catchpad within %cs1 []
+ ; CHECK-NEXT: %[[CatchPad:[^ ]+]] = catchpad within %cs1 [ptr null, i32 0, ptr null]
+ %cp = catchpad within %cs1 [ptr null, i32 0, ptr null]
%b = call i1 @i() [ "funclet"(token %cp) ]
br i1 %b, label %left, label %right
left:
diff --git a/llvm/test/CodeGen/WinEH/wineh-malformed-catchpad.ll b/llvm/test/CodeGen/WinEH/wineh-malformed-catchpad.ll
new file mode 100644
index 0000000000000..6d769e285baad
--- /dev/null
+++ b/llvm/test/CodeGen/WinEH/wineh-malformed-catchpad.ll
@@ -0,0 +1,203 @@
+; RUN: split-file %s %t
+; RUN: llc -o - %t/issue219223_cxx.ll | FileCheck %t/issue219223_cxx.ll
+; RUN: llc -o - %t/issue219223_seh.ll | FileCheck %t/issue219223_seh.ll
+; RUN: llc -o - %t/issue219223_clr.ll | FileCheck %t/issue219223_clr.ll
+; RUN: opt -mtriple=x86_64-pc-windows-msvc -S -passes=win-eh-prepare < %t/single.ll | FileCheck %t/single.ll
+; RUN: opt -mtriple=x86_64-pc-windows-msvc -S -passes=win-eh-prepare < %t/sibling_cxx.ll | FileCheck %t/sibling_cxx.ll
+; RUN: opt -mtriple=x86_64-pc-windows-msvc -S -passes=win-eh-prepare < %t/sibling_seh.ll | FileCheck %t/sibling_seh.ll
+; RUN: opt -mtriple=x86_64-pc-windows-msvc -S -passes=win-eh-prepare < %t/sibling_clr.ll | FileCheck %t/sibling_clr.ll
+
+;--- issue219223_cxx.ll
+target triple = "x86_64-pc-linux-gnu"
+
+declare i32 @__CxxFrameHandler3(...)
+declare void @f()
+
+define void @empty_catchpad_cxx() personality ptr @__CxxFrameHandler3 {
+entry:
+ invoke void @f() to label %cont unwind label %dispatch
+
+dispatch:
+ %cs = catchswitch within none [label %catch] unwind to caller
+
+catch:
+ %pad = catchpad within %cs []
+ catchret from %pad to label %cont
+
+cont:
+ ret void
+}
+
+; CHECK-LABEL: empty_catchpad_cxx:
+
+;--- issue219223_seh.ll
+declare i32 @__C_specific_handler(...)
+declare void @f()
+
+define void @empty_catchpad_seh() personality ptr @__C_specific_handler {
+entry:
+ invoke void @f() to label %cont unwind label %dispatch
+
+dispatch:
+ %cs = catchswitch within none [label %catch] unwind to caller
+
+catch:
+ %pad = catchpad within %cs []
+ catchret from %pad to label %cont
+
+cont:
+ ret void
+}
+
+; CHECK-LABEL: empty_catchpad_seh:
+
+;--- issue219223_clr.ll
+declare void @ProcessCLRException(...)
+declare void @f()
+
+define void @empty_catchpad_clr() personality ptr @ProcessCLRException {
+entry:
+ invoke void @f() to label %cont unwind label %dispatch
+
+dispatch:
+ %cs = catchswitch within none [label %catch] unwind to caller
+
+catch:
+ %pad = catchpad within %cs []
+ catchret from %pad to label %cont
+
+cont:
+ ret void
+}
+
+; CHECK-LABEL: empty_catchpad_clr:
+
+;--- single.ll
+declare i32 @__CxxFrameHandler3(...)
+declare void @f()
+
+define void @malformed_single() personality ptr @__CxxFrameHandler3 {
+entry:
+ invoke void @f()
+ to label %cont unwind label %dispatch
+
+dispatch:
+ %cs = catchswitch within none [label %catch] unwind to caller
+
+catch:
+ %pad = catchpad within %cs []
+ catchret from %pad to label %cont
+
+cont:
+ ret void
+}
+
+; CHECK-LABEL: define void @malformed_single()
+; CHECK: %cs = catchswitch within none [label %catch] unwind to caller
+; CHECK: catch:
+; CHECK-NEXT: %pad = catchpad within %cs []
+; CHECK-NEXT: unreachable
+; CHECK: cont: ; preds = %entry
+; CHECK-NEXT: ret void
+
+;--- sibling_cxx.ll
+declare i32 @__CxxFrameHandler3(...)
+declare void @f()
+
+define void @sibling_cxx() personality ptr @__CxxFrameHandler3 {
+entry:
+ invoke void @f()
+ to label %cont unwind label %dispatch
+
+dispatch:
+ %cs = catchswitch within none [label %catch.bad, label %catch.good] unwind to caller
+
+catch.bad:
+ %pad.bad = catchpad within %cs [ptr null]
+ catchret from %pad.bad to label %cont
+
+catch.good:
+ %pad.good = catchpad within %cs [ptr null, i32 0, ptr null]
+ catchret from %pad.good to label %cont
+
+cont:
+ ret void
+}
+
+; CHECK-LABEL: define void @sibling_cxx()
+; CHECK: catch.bad:
+; CHECK-NEXT: %pad.bad = catchpad within %cs [ptr null]
+; CHECK-NEXT: unreachable
+; CHECK: catch.good:
+; CHECK-NEXT: %pad.good = catchpad within %cs [ptr null, i32 0, ptr null]
+; CHECK-NEXT: unreachable
+; CHECK: cont: ; preds = %entry
+; CHECK-NEXT: ret void
+
+;--- sibling_seh.ll
+declare i32 @__C_specific_handler(...)
+declare void @f()
+
+define void @sibling_seh() personality ptr @__C_specific_handler {
+entry:
+ invoke void @f()
+ to label %cont unwind label %dispatch
+
+dispatch:
+ %cs = catchswitch within none [label %catch.bad, label %catch.good] unwind to caller
+
+catch.bad:
+ %pad.bad = catchpad within %cs []
+ catchret from %pad.bad to label %cont
+
+catch.good:
+ %pad.good = catchpad within %cs [ptr null]
+ catchret from %pad.good to label %cont
+
+cont:
+ ret void
+}
+
+; CHECK-LABEL: define void @sibling_seh()
+; CHECK: catch.bad:
+; CHECK-NEXT: %pad.bad = catchpad within %cs []
+; CHECK-NEXT: unreachable
+; CHECK: catch.good:
+; CHECK-NEXT: %pad.good = catchpad within %cs [ptr null]
+; CHECK-NEXT: unreachable
+; CHECK: cont: ; preds = %entry
+; CHECK-NEXT: ret void
+
+;--- sibling_clr.ll
+declare void @ProcessCLRException(...)
+declare void @f()
+
+define void @sibling_clr() personality ptr @ProcessCLRException {
+entry:
+ invoke void @f()
+ to label %cont unwind label %dispatch
+
+dispatch:
+ %cs = catchswitch within none [label %catch.bad, label %catch.good] unwind to caller
+
+catch.bad:
+ %pad.bad = catchpad within %cs []
+ catchret from %pad.bad to label %cont
+
+catch.good:
+ %pad.good = catchpad within %cs [i32 1]
+ catchret from %pad.good to label %cont
+
+cont:
+ ret void
+}
+
+; CHECK-LABEL: define void @sibling_clr()
+; CHECK: catch.bad:
+; CHECK-NEXT: %pad.bad = catchpad within %cs []
+; CHECK-NEXT: unreachable
+; CHECK: catch.good:
+; CHECK-NEXT: %pad.good = catchpad within %cs [i32 1]
+; CHECK-NEXT: unreachable
+; CHECK: cont: ; preds = %entry
+; CHECK-NEXT: ret void
>From eba06dce2232700a5c548fbb02fd69f37ec9427d Mon Sep 17 00:00:00 2001
From: Aaron Danen <aaron.danen at gmail.com>
Date: Wed, 9 Sep 2026 14:06:48 -0700
Subject: [PATCH 2/2] added a scan and diagnostic
---
llvm/lib/CodeGen/WinEHPrepare.cpp | 18 +++++++++++++++
.../CodeGen/WinEH/wineh-malformed-catchpad.ll | 23 ++++++++++++-------
2 files changed, 33 insertions(+), 8 deletions(-)
diff --git a/llvm/lib/CodeGen/WinEHPrepare.cpp b/llvm/lib/CodeGen/WinEHPrepare.cpp
index 272291dd2f05c..0d81cecd81168 100644
--- a/llvm/lib/CodeGen/WinEHPrepare.cpp
+++ b/llvm/lib/CodeGen/WinEHPrepare.cpp
@@ -24,6 +24,7 @@
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/WinEHFuncInfo.h"
#include "llvm/IR/Constants.h"
+#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/EHPersonalities.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
@@ -137,6 +138,7 @@ class WinEHPrepareImpl {
DenseMap<BasicBlock *, Value *> &Loads, Function &F);
bool prepareExplicitEH(Function &F);
void colorFunclets(Function &F);
+ void diagnoseMalformedCatchpads(Function &F);
bool demotePHIsOnFunclets(Function &F, bool DemoteCatchSwitchPHIOnly);
bool cloneCommonBlocks(Function &F);
@@ -200,6 +202,9 @@ bool WinEHPrepareImpl::runOnFunction(Function &Fn) {
if (!isScopedEHPersonality(Personality))
return false;
+ // Report a diagnostic for each malformed catchpad
+ diagnoseMalformedCatchpads(Fn);
+
DL = &Fn.getDataLayout();
return prepareExplicitEH(Fn);
}
@@ -1335,6 +1340,19 @@ void WinEHPrepareImpl::verifyPreparedFunclets(Function &F) {
}
#endif
+void WinEHPrepareImpl::diagnoseMalformedCatchpads(Function &F) {
+ for (BasicBlock &BB : F) {
+ for (Instruction &I : BB) {
+ if (CatchPadInst *CPI = dyn_cast<CatchPadInst>(&I)) {
+ if (isMalformedCatchpad(CPI, Personality)) {
+ F.getContext().diagnose(DiagnosticInfoGenericWithLoc(
+ "catchpad with unexpected arguments", F, CPI->getDebugLoc()));
+ }
+ }
+ }
+ }
+}
+
bool WinEHPrepareImpl::prepareExplicitEH(Function &F) {
// Remove unreachable blocks. It is not valuable to assign them a color and
// their existence can trick us into thinking values are alive when they are
diff --git a/llvm/test/CodeGen/WinEH/wineh-malformed-catchpad.ll b/llvm/test/CodeGen/WinEH/wineh-malformed-catchpad.ll
index 6d769e285baad..9baef294cfc80 100644
--- a/llvm/test/CodeGen/WinEH/wineh-malformed-catchpad.ll
+++ b/llvm/test/CodeGen/WinEH/wineh-malformed-catchpad.ll
@@ -1,14 +1,15 @@
; RUN: split-file %s %t
-; RUN: llc -o - %t/issue219223_cxx.ll | FileCheck %t/issue219223_cxx.ll
-; RUN: llc -o - %t/issue219223_seh.ll | FileCheck %t/issue219223_seh.ll
-; RUN: llc -o - %t/issue219223_clr.ll | FileCheck %t/issue219223_clr.ll
-; RUN: opt -mtriple=x86_64-pc-windows-msvc -S -passes=win-eh-prepare < %t/single.ll | FileCheck %t/single.ll
-; RUN: opt -mtriple=x86_64-pc-windows-msvc -S -passes=win-eh-prepare < %t/sibling_cxx.ll | FileCheck %t/sibling_cxx.ll
-; RUN: opt -mtriple=x86_64-pc-windows-msvc -S -passes=win-eh-prepare < %t/sibling_seh.ll | FileCheck %t/sibling_seh.ll
-; RUN: opt -mtriple=x86_64-pc-windows-msvc -S -passes=win-eh-prepare < %t/sibling_clr.ll | FileCheck %t/sibling_clr.ll
+; RUN: not llc -o - %t/issue219223_cxx.ll 2>/dev/null | FileCheck %t/issue219223_cxx.ll
+; RUN: not llc -o - %t/issue219223_seh.ll 2>/dev/null | FileCheck %t/issue219223_seh.ll
+; RUN: not llc -o - %t/issue219223_clr.ll 2>/dev/null | FileCheck %t/issue219223_clr.ll
+; RUN: not opt -mtriple=x86_64-pc-windows-msvc -S -passes=win-eh-prepare < %t/single.ll 2>/dev/null | FileCheck %t/single.ll
+; RUN: not opt -mtriple=x86_64-pc-windows-msvc -S -passes=win-eh-prepare < %t/single.ll 2>&1 >/dev/null | FileCheck %t/single.ll --check-prefix=DIAG
+; RUN: not opt -mtriple=x86_64-pc-windows-msvc -S -passes=win-eh-prepare < %t/sibling_cxx.ll 2>/dev/null | FileCheck %t/sibling_cxx.ll
+; RUN: not opt -mtriple=x86_64-pc-windows-msvc -S -passes=win-eh-prepare < %t/sibling_seh.ll 2>/dev/null | FileCheck %t/sibling_seh.ll
+; RUN: not opt -mtriple=x86_64-pc-windows-msvc -S -passes=win-eh-prepare < %t/sibling_clr.ll 2>/dev/null | FileCheck %t/sibling_clr.ll
;--- issue219223_cxx.ll
-target triple = "x86_64-pc-linux-gnu"
+target triple = "x86_64-pc-windows-msvc"
declare i32 @__CxxFrameHandler3(...)
declare void @f()
@@ -31,6 +32,8 @@ cont:
; CHECK-LABEL: empty_catchpad_cxx:
;--- issue219223_seh.ll
+target triple = "x86_64-pc-windows-msvc"
+
declare i32 @__C_specific_handler(...)
declare void @f()
@@ -52,6 +55,8 @@ cont:
; CHECK-LABEL: empty_catchpad_seh:
;--- issue219223_clr.ll
+target triple = "x86_64-pc-windows-msvc"
+
declare void @ProcessCLRException(...)
declare void @f()
@@ -100,6 +105,8 @@ cont:
; CHECK: cont: ; preds = %entry
; CHECK-NEXT: ret void
+; DIAG: catchpad with unexpected arguments
+
;--- sibling_cxx.ll
declare i32 @__CxxFrameHandler3(...)
declare void @f()
More information about the llvm-commits
mailing list