diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -73,6 +73,7 @@ #include "llvm/IR/DebugLoc.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Dominators.h" +#include "llvm/IR/EHPersonalities.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalAlias.h" #include "llvm/IR/GlobalValue.h" @@ -327,6 +328,10 @@ // terminators that indicate the unwind, used to detect cycles therein. MapVector SiblingFuncletInfo; + /// Cache which blocks are in which funclet, if an EH funclet personality is + /// in use. Otherwise empty. + DenseMap BlockEHFuncletColors; + /// Cache of constants visited in search of ConstantExprs. SmallPtrSet ConstantExprVisited; @@ -2612,6 +2617,9 @@ F.getParent(), Per, Per->getParent()); } + // EH funclet coloring can be expensive, recompute on-demand + BlockEHFuncletColors.clear(); + if (F.isMaterializable()) { // Function has a body somewhere we can't see. Check(MDs.empty(), "unmaterialized function cannot have metadata", &F, @@ -5745,6 +5753,40 @@ break; } }; + + // Verify that there aren't any unmediated control transfers between funclets. + if (IntrinsicInst::mayLowerToFunctionCall(ID)) { + Function *F = Call.getParent()->getParent(); + if (F->hasPersonalityFn() && + isScopedEHPersonality(classifyEHPersonality(F->getPersonalityFn()))) { + // Run EH funclet coloring on-demand and cache results for other intrinsic + // calls in this function + if (BlockEHFuncletColors.empty()) + BlockEHFuncletColors = colorEHFunclets(*F); + + // Check for catch-/cleanup-pad in first funclet block + bool InEHFunclet = false; + BasicBlock *CallBB = Call.getParent(); + const ColorVector &CV = BlockEHFuncletColors.find(CallBB)->second; + assert(CV.size() > 0 && "Uncolored block"); + for (BasicBlock *ColorFirstBB : CV) + if (dyn_cast_or_null(ColorFirstBB->getFirstNonPHI())) + InEHFunclet = true; + + // Check for funclet operand bundle + bool HasToken = false; + for (unsigned I = 0, E = Call.getNumOperandBundles(); I != E; ++I) + if (Call.getOperandBundleAt(I).getTagID() == LLVMContext::OB_funclet) + HasToken = true; + + // These cases can cause silent code truncations in WinEHPrepare + if (InEHFunclet) { + Check(HasToken, "Missing funclet token on intrinsic call", &Call); + } else { + Check(!HasToken, "Dangling funclet token on intrinsic call", &Call); + } + } + } } /// Carefully grab the subprogram from a local scope. diff --git a/llvm/test/Transforms/ObjCARC/invoke-2.ll b/llvm/test/Transforms/ObjCARC/invoke-2.ll --- a/llvm/test/Transforms/ObjCARC/invoke-2.ll +++ b/llvm/test/Transforms/ObjCARC/invoke-2.ll @@ -39,7 +39,7 @@ %4 = catchpad within %2 [i8* null, i32 0, i8* null] %exn.adjusted = tail call i8* @llvm.objc.begin_catch(i8* undef) tail call void @llvm.objc.end_catch(), !clang.arc.no_objc_arc_exceptions !0 - br label %eh.cont + catchret from %4 to label %eh.cont } ; CHECK-LABEL: @f diff --git a/llvm/test/Verifier/operand-bundles-wineh.ll b/llvm/test/Verifier/operand-bundles-wineh.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/operand-bundles-wineh.ll @@ -0,0 +1,50 @@ +; RUN: not opt -verify < %s 2>&1 | FileCheck %s + +define void @report_missing() personality ptr @__CxxFrameHandler3 { +entry: + invoke void @may_throw() to label %eh.cont unwind label %catch.dispatch + +catch.dispatch: + %0 = catchswitch within none [label %catch] unwind to caller + +catch: + %1 = catchpad within %0 [ptr null, i32 0, ptr null] + br label %catch.cont + +catch.cont: +; CHECK: Missing funclet token on intrinsic call + %2 = call ptr @llvm.objc.retain(ptr null) + catchret from %1 to label %eh.cont + +eh.cont: + ret void +} + +define void @report_dangling() personality ptr @__CxxFrameHandler3 { +entry: + invoke void @may_throw() to label %eh.cont unwind label %catch.dispatch + +catch.dispatch: + %0 = catchswitch within none [label %catch] unwind to caller + +catch: + %1 = catchpad within %0 [ptr null, i32 0, ptr null] + call void @do_something() + catchret from %1 to label %catchret.dest + +catchret.dest: +; CHECK: Dangling funclet token on intrinsic call + %2 = call ptr @llvm.objc.retain(ptr null) [ "funclet"(token %1) ] + br label %eh.cont + +eh.cont: + ret void +} + +declare void @may_throw() +declare void @do_something() +declare i32 @__CxxFrameHandler3(...) + +declare ptr @llvm.objc.retain(ptr) #0 + +attributes #0 = { nounwind }