diff --git a/llvm/lib/CodeGen/WinEHPrepare.cpp b/llvm/lib/CodeGen/WinEHPrepare.cpp --- a/llvm/lib/CodeGen/WinEHPrepare.cpp +++ b/llvm/lib/CodeGen/WinEHPrepare.cpp @@ -55,6 +55,12 @@ "demote-catchswitch-only", cl::Hidden, cl::desc("Demote catchswitch BBs only (for wasm EH)"), cl::init(false)); +static cl::opt VerifyFuncletTokens( + "verify-funclet-tokens", cl::Hidden, + cl::desc("During WinEH preparations check for funclet tokens outside of " + "actual funclets"), + cl::init(false)); + namespace { class WinEHPrepare : public FunctionPass { @@ -89,6 +95,7 @@ void removeImplausibleInstructions(Function &F); void cleanupPreparedFunclets(Function &F); void verifyPreparedFunclets(Function &F); + void verifyNoFuncletTokens(const std::vector &Blocks); bool DemoteCatchSwitchPHIOnly; @@ -705,6 +712,13 @@ for (BasicBlock *Color : Colors) FuncletBlocks[Color].push_back(&BB); } + + if (VerifyFuncletTokens) + for (auto &Funclet : FuncletBlocks) { + auto *FuncletPad = dyn_cast(Funclet.first->getFirstNonPHI()); + if (!FuncletPad) + verifyNoFuncletTokens(Funclet.second); + } } void WinEHPrepare::demotePHIsOnFunclets(Function &F, @@ -1043,6 +1057,23 @@ } #endif +void WinEHPrepare::verifyNoFuncletTokens(const std::vector &Blocks) { + for (BasicBlock *BB : Blocks) { + for (Instruction &I : *BB) { + if (auto *CB = dyn_cast(&I)) { + if (auto BU = CB->getOperandBundle(LLVMContext::OB_funclet)) { + std::string Buffer; + raw_string_ostream OS(Buffer); + OS << "Invalid funclet token in BasicBlock " << BB->getName(); + Value *FuncletBundleOperand = BU->Inputs.front(); + OS << " outside funclet:" << *FuncletBundleOperand << "\n"; + report_fatal_error(OS.str().c_str()); + } + } + } + } +} + bool WinEHPrepare::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-funclet-bundle-validation.ll b/llvm/test/CodeGen/WinEH/wineh-funclet-bundle-validation.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WinEH/wineh-funclet-bundle-validation.ll @@ -0,0 +1,33 @@ +; RUN: not llc < %s -mtriple=x86_64-windows-msvc -verify-funclet-tokens -o - 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR + +declare void @opaque() + +declare i32 @__CxxFrameHandler3(...) + +declare void @llvm.objc.storeStrong(ptr, ptr) #0 + +define void @invalid_funclet_token() personality ptr @__CxxFrameHandler3 { +entry: + invoke void @opaque() + to label %invoke.cont unwind label %catch.dispatch + +catch.dispatch: ; preds = %entry + %0 = catchswitch within none [label %catch] unwind to caller + +invoke.cont: ; preds = %entry + br label %eh.cont + +eh.cont: ; preds = %catchret.dest, %invoke.cont + ret void + +catch: ; preds = %catch.dispatch + %1 = catchpad within %0 [ptr null, i32 0, ptr null] + catchret from %1 to label %catchret.dest + +; CHECK-ERROR: LLVM ERROR: Invalid funclet token in BasicBlock catchret.dest outside funclet +catchret.dest: ; preds = %catch + call void @llvm.objc.storeStrong(ptr null, ptr null) [ "funclet"(token %1) ] + br label %eh.cont +} + +attributes #0 = { nounwind }