diff --git a/llvm/lib/Analysis/EHPersonalities.cpp b/llvm/lib/Analysis/EHPersonalities.cpp --- a/llvm/lib/Analysis/EHPersonalities.cpp +++ b/llvm/lib/Analysis/EHPersonalities.cpp @@ -11,12 +11,21 @@ #include "llvm/ADT/Triple.h" #include "llvm/IR/CFG.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/Function.h" #include "llvm/IR/Instructions.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; +static cl::opt NoFuncletBundleErrors( + "no-funclet-bundle-errors", cl::Hidden, + cl::desc("Bail out if validation of funclet bundle operands fails during " + "EH funclet coloring"), + cl::init(false)); + /// See if the given exception handling personality function is one that we /// understand. If so, return a description of it; otherwise return Unknown. EHPersonality llvm::classifyEHPersonality(const Value *Pers) { @@ -82,6 +91,23 @@ return !isAsynchronousEHPersonality(Personality); } +class InvalidFuncletTokenDiagnostic : public DiagnosticInfo { + std::string Msg; + +public: + InvalidFuncletTokenDiagnostic(DiagnosticSeverity Severity, + StringRef BasicBlock, FuncletPadInst *ParentPad, + Value *ReferredPad) + : DiagnosticInfo(DK_Linker, Severity) { + raw_string_ostream OS(Msg); + OS << "Invalid funclet token in BasicBlock " << BasicBlock; + if (!ParentPad) + OS << " (outside funclet)"; + OS << *ReferredPad; + } + void print(DiagnosticPrinter &DP) const override { DP << Msg; } +}; + DenseMap llvm::colorEHFunclets(Function &F) { SmallVector, 16> Worklist; BasicBlock *EntryBlock = &F.getEntryBlock(); @@ -121,6 +147,20 @@ else continue; + // Make sure all instructions with funclet tokens refer to the current pad. + auto *FuncletPad = dyn_cast(Color->getFirstNonPHI()); + for (Instruction &I : *Visiting) { + if (auto *CB = dyn_cast(&I)) { + if (auto BU = CB->getOperandBundle(LLVMContext::OB_funclet)) { + Value *FuncletBundleOperand = BU->Inputs.front(); + if (FuncletBundleOperand != FuncletPad) + F.getContext().diagnose(InvalidFuncletTokenDiagnostic( + NoFuncletBundleErrors ? DS_Warning : DS_Error, + Visiting->getName(), FuncletPad, FuncletBundleOperand)); + } + } + } + DEBUG_WITH_TYPE("winehprepare-coloring", dbgs() << " Assigned color \'" << Color->getName() << "\' to block \'" << Visiting->getName() 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: llc < %s -mtriple=x86_64-windows-msvc -no-funclet-bundle-errors -o - 2>&1 | FileCheck %s --check-prefix=CHECK-WARNING + +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-WARNING: warning: 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 }