LLVM 23.0.0git
DwarfEHPrepare.cpp
Go to the documentation of this file.
1//===- DwarfEHPrepare - Prepare exception handling for code generation ----===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This pass mulches exception handling code into a form adapted to code
10// generation. Required if using dwarf exception handling.
11//
12//===----------------------------------------------------------------------===//
13
15#include "llvm/ADT/BitVector.h"
17#include "llvm/ADT/Statistic.h"
18#include "llvm/Analysis/CFG.h"
24#include "llvm/IR/BasicBlock.h"
25#include "llvm/IR/Constants.h"
28#include "llvm/IR/Dominators.h"
30#include "llvm/IR/Function.h"
32#include "llvm/IR/Module.h"
33#include "llvm/IR/Type.h"
35#include "llvm/Pass.h"
40#include <cstddef>
41
42using namespace llvm;
43
44#define DEBUG_TYPE "dwarf-eh-prepare"
45
46STATISTIC(NumResumesLowered, "Number of resume calls lowered");
47STATISTIC(NumCleanupLandingPadsUnreachable,
48 "Number of cleanup landing pads found unreachable");
49STATISTIC(NumCleanupLandingPadsRemaining,
50 "Number of cleanup landing pads remaining");
51STATISTIC(NumNoUnwind, "Number of functions with nounwind");
52STATISTIC(NumUnwind, "Number of functions with unwind");
53
54namespace {
55
56class DwarfEHPrepare {
57 CodeGenOptLevel OptLevel;
58
59 Function &F;
60 const LibcallLoweringInfo &Libcalls;
61 DomTreeUpdater *DTU;
63 const Triple &TargetTriple;
64
65 /// Return the exception object from the value passed into
66 /// the 'resume' instruction (typically an aggregate). Clean up any dead
67 /// instructions, including the 'resume' instruction.
68 Value *GetExceptionObject(ResumeInst *RI);
69
70 /// Replace resumes that are not reachable from a cleanup landing pad with
71 /// unreachable and then simplify those blocks.
72 size_t
73 pruneUnreachableResumes(SmallVectorImpl<ResumeInst *> &Resumes,
75
76 /// Convert the ResumeInsts that are still present
77 /// into calls to the appropriate _Unwind_Resume function.
78 bool InsertUnwindResumeCalls();
79
80public:
81 DwarfEHPrepare(CodeGenOptLevel OptLevel_, Function &F_,
82 const LibcallLoweringInfo &Libcalls_, DomTreeUpdater *DTU_,
83 const TargetTransformInfo *TTI_, const Triple &TargetTriple_)
84 : OptLevel(OptLevel_), F(F_), Libcalls(Libcalls_), DTU(DTU_), TTI(TTI_),
85 TargetTriple(TargetTriple_) {}
86
87 bool run();
88};
89
90} // namespace
91
92Value *DwarfEHPrepare::GetExceptionObject(ResumeInst *RI) {
93 Value *V = RI->getOperand(0);
94 Value *ExnObj = nullptr;
95 InsertValueInst *SelIVI = dyn_cast<InsertValueInst>(V);
96 LoadInst *SelLoad = nullptr;
97 InsertValueInst *ExcIVI = nullptr;
98 bool EraseIVIs = false;
99
100 if (SelIVI) {
101 if (SelIVI->getNumIndices() == 1 && *SelIVI->idx_begin() == 1) {
102 ExcIVI = dyn_cast<InsertValueInst>(SelIVI->getOperand(0));
103 if (ExcIVI && isa<UndefValue>(ExcIVI->getOperand(0)) &&
104 ExcIVI->getNumIndices() == 1 && *ExcIVI->idx_begin() == 0) {
105 ExnObj = ExcIVI->getOperand(1);
106 SelLoad = dyn_cast<LoadInst>(SelIVI->getOperand(1));
107 EraseIVIs = true;
108 }
109 }
110 }
111
112 if (!ExnObj)
113 ExnObj = ExtractValueInst::Create(RI->getOperand(0), 0, "exn.obj",
114 RI->getIterator());
115
116 RI->eraseFromParent();
117
118 if (EraseIVIs) {
119 if (SelIVI->use_empty())
120 SelIVI->eraseFromParent();
121 if (ExcIVI->use_empty())
122 ExcIVI->eraseFromParent();
123 if (SelLoad && SelLoad->use_empty())
124 SelLoad->eraseFromParent();
125 }
126
127 return ExnObj;
128}
129
130size_t DwarfEHPrepare::pruneUnreachableResumes(
131 SmallVectorImpl<ResumeInst *> &Resumes,
132 SmallVectorImpl<LandingPadInst *> &CleanupLPads) {
133 assert(DTU && "Should have DomTreeUpdater here.");
134
135 BitVector ResumeReachable(Resumes.size());
136 size_t ResumeIndex = 0;
137 for (auto *RI : Resumes) {
138 for (auto *LP : CleanupLPads) {
139 if (isPotentiallyReachable(LP, RI, nullptr, &DTU->getDomTree())) {
140 ResumeReachable.set(ResumeIndex);
141 break;
142 }
143 }
144 ++ResumeIndex;
145 }
146
147 // If everything is reachable, there is no change.
148 if (ResumeReachable.all())
149 return Resumes.size();
150
151 LLVMContext &Ctx = F.getContext();
152
153 // Otherwise, insert unreachable instructions and call simplifycfg.
154 size_t ResumesLeft = 0;
155 for (size_t I = 0, E = Resumes.size(); I < E; ++I) {
156 ResumeInst *RI = Resumes[I];
157 if (ResumeReachable[I]) {
158 Resumes[ResumesLeft++] = RI;
159 } else {
160 BasicBlock *BB = RI->getParent();
161 new UnreachableInst(Ctx, RI->getIterator());
162 RI->eraseFromParent();
163 simplifyCFG(BB, *TTI, DTU);
164 }
165 }
166 Resumes.resize(ResumesLeft);
167 return ResumesLeft;
168}
169
170bool DwarfEHPrepare::InsertUnwindResumeCalls() {
173 if (F.doesNotThrow())
174 NumNoUnwind++;
175 else
176 NumUnwind++;
177 for (BasicBlock &BB : F) {
178 if (auto *RI = dyn_cast<ResumeInst>(BB.getTerminator()))
179 Resumes.push_back(RI);
180 if (auto *LP = BB.getLandingPadInst())
181 if (LP->isCleanup())
182 CleanupLPads.push_back(LP);
183 }
184
185 NumCleanupLandingPadsRemaining += CleanupLPads.size();
186
187 if (Resumes.empty())
188 return false;
189
190 // Check the personality, don't do anything if it's scope-based.
191 EHPersonality Pers = classifyEHPersonality(F.getPersonalityFn());
192 if (isScopedEHPersonality(Pers))
193 return false;
194
195 LLVMContext &Ctx = F.getContext();
196
197 size_t ResumesLeft = Resumes.size();
198 if (OptLevel != CodeGenOptLevel::None) {
199 ResumesLeft = pruneUnreachableResumes(Resumes, CleanupLPads);
200#if LLVM_ENABLE_STATS
201 unsigned NumRemainingLPs = 0;
202 for (BasicBlock &BB : F) {
203 if (auto *LP = BB.getLandingPadInst())
204 if (LP->isCleanup())
205 NumRemainingLPs++;
206 }
207 NumCleanupLandingPadsUnreachable += CleanupLPads.size() - NumRemainingLPs;
208 NumCleanupLandingPadsRemaining -= CleanupLPads.size() - NumRemainingLPs;
209#endif
210 }
211
212 if (ResumesLeft == 0)
213 return true; // We pruned them all.
214
215 // RewindFunction - _Unwind_Resume or the target equivalent.
216 FunctionCallee RewindFunction;
217 CallingConv::ID RewindFunctionCallingConv;
218 FunctionType *FTy;
219 StringRef RewindName;
220 bool DoesRewindFunctionNeedExceptionObject;
221
222 if ((Pers == EHPersonality::GNU_CXX || Pers == EHPersonality::GNU_CXX_SjLj) &&
223 TargetTriple.isTargetEHABICompatible()) {
224 RewindName = Libcalls.getLibcallName(RTLIB::CXA_END_CLEANUP);
225 FTy = FunctionType::get(Type::getVoidTy(Ctx), false);
226 RewindFunctionCallingConv =
227 Libcalls.getLibcallCallingConv(RTLIB::CXA_END_CLEANUP);
228 DoesRewindFunctionNeedExceptionObject = false;
229 } else {
230 RewindName = Libcalls.getLibcallName(RTLIB::UNWIND_RESUME);
231 FTy = FunctionType::get(Type::getVoidTy(Ctx), PointerType::getUnqual(Ctx),
232 false);
233 RewindFunctionCallingConv =
234 Libcalls.getLibcallCallingConv(RTLIB::UNWIND_RESUME);
235 DoesRewindFunctionNeedExceptionObject = true;
236 }
237 RewindFunction = F.getParent()->getOrInsertFunction(RewindName, FTy);
238
239 // Create the basic block where the _Unwind_Resume call will live.
240 if (ResumesLeft == 1) {
241 // Instead of creating a new BB and PHI node, just append the call to
242 // _Unwind_Resume to the end of the single resume block.
243 ResumeInst *RI = Resumes.front();
244 BasicBlock *UnwindBB = RI->getParent();
245 Value *ExnObj = GetExceptionObject(RI);
246 llvm::SmallVector<Value *, 1> RewindFunctionArgs;
247 if (DoesRewindFunctionNeedExceptionObject)
248 RewindFunctionArgs.push_back(ExnObj);
249
250 // Call the rewind function.
251 CallInst *CI =
252 CallInst::Create(RewindFunction, RewindFunctionArgs, "", UnwindBB);
253 // The verifier requires that all calls of debug-info-bearing functions
254 // from debug-info-bearing functions have a debug location (for inlining
255 // purposes). Assign a dummy location to satisfy the constraint.
256 Function *RewindFn = dyn_cast<Function>(RewindFunction.getCallee());
257 if (RewindFn && RewindFn->getSubprogram())
258 if (DISubprogram *SP = F.getSubprogram())
259 CI->setDebugLoc(DILocation::get(SP->getContext(), 0, 0, SP));
260 CI->setCallingConv(RewindFunctionCallingConv);
261
262 // We never expect _Unwind_Resume to return.
263 CI->setDoesNotReturn();
264 new UnreachableInst(Ctx, UnwindBB);
265 return true;
266 }
267
268 std::vector<DominatorTree::UpdateType> Updates;
269 Updates.reserve(Resumes.size());
270
271 llvm::SmallVector<Value *, 1> RewindFunctionArgs;
272
273 BasicBlock *UnwindBB = BasicBlock::Create(Ctx, "unwind_resume", &F);
274 PHINode *PN = PHINode::Create(PointerType::getUnqual(Ctx), ResumesLeft,
275 "exn.obj", UnwindBB);
276
277 // Extract the exception object from the ResumeInst and add it to the PHI node
278 // that feeds the _Unwind_Resume call.
279 for (ResumeInst *RI : Resumes) {
280 BasicBlock *Parent = RI->getParent();
281 BranchInst::Create(UnwindBB, Parent);
282 Updates.push_back({DominatorTree::Insert, Parent, UnwindBB});
283
284 Value *ExnObj = GetExceptionObject(RI);
285 PN->addIncoming(ExnObj, Parent);
286
287 ++NumResumesLowered;
288 }
289
290 if (DoesRewindFunctionNeedExceptionObject)
291 RewindFunctionArgs.push_back(PN);
292
293 // Call the function.
294 CallInst *CI =
295 CallInst::Create(RewindFunction, RewindFunctionArgs, "", UnwindBB);
296 // The verifier requires that all calls of debug-info-bearing functions
297 // from debug-info-bearing functions have a debug location (for inlining
298 // purposes). Assign a dummy location to satisfy the constraint.
299 Function *RewindFn = dyn_cast<Function>(RewindFunction.getCallee());
300 if (RewindFn && RewindFn->getSubprogram())
301 if (DISubprogram *SP = F.getSubprogram())
302 CI->setDebugLoc(DILocation::get(SP->getContext(), 0, 0, SP));
303 CI->setCallingConv(RewindFunctionCallingConv);
304
305 // We never expect _Unwind_Resume to return.
306 CI->setDoesNotReturn();
307 new UnreachableInst(Ctx, UnwindBB);
308
309 if (DTU)
310 DTU->applyUpdates(Updates);
311
312 return true;
313}
314
315bool DwarfEHPrepare::run() {
316 bool Changed = InsertUnwindResumeCalls();
317
318 return Changed;
319}
320
322 const LibcallLoweringInfo &Libcalls,
324 const Triple &TargetTriple) {
325 DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Lazy);
326
327 return DwarfEHPrepare(OptLevel, F, Libcalls, DT ? &DTU : nullptr, TTI,
328 TargetTriple)
329 .run();
330}
331
332namespace {
333
334class DwarfEHPrepareLegacyPass : public FunctionPass {
335
336 CodeGenOptLevel OptLevel;
337
338public:
339 static char ID; // Pass identification, replacement for typeid.
340
341 DwarfEHPrepareLegacyPass(CodeGenOptLevel OptLevel = CodeGenOptLevel::Default)
342 : FunctionPass(ID), OptLevel(OptLevel) {}
343
344 bool runOnFunction(Function &F) override {
345 const TargetMachine &TM =
346 getAnalysis<TargetPassConfig>().getTM<TargetMachine>();
347 const TargetSubtargetInfo *Subtarget = TM.getSubtargetImpl(F);
348
349 const LibcallLoweringInfo &Libcalls =
350 getAnalysis<LibcallLoweringInfoWrapper>().getLibcallLowering(
351 *F.getParent(), *Subtarget);
352
353 DominatorTree *DT = nullptr;
354 const TargetTransformInfo *TTI = nullptr;
355 if (auto *DTWP = getAnalysisIfAvailable<DominatorTreeWrapperPass>())
356 DT = &DTWP->getDomTree();
357 if (OptLevel != CodeGenOptLevel::None) {
358 if (!DT)
359 DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
360 TTI = &getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F);
361 }
362 return prepareDwarfEH(OptLevel, F, Libcalls, DT, TTI, TM.getTargetTriple());
363 }
364
365 void getAnalysisUsage(AnalysisUsage &AU) const override {
366 AU.addRequired<LibcallLoweringInfoWrapper>();
367 AU.addRequired<TargetPassConfig>();
368 AU.addRequired<TargetTransformInfoWrapperPass>();
369 if (OptLevel != CodeGenOptLevel::None) {
370 AU.addRequired<DominatorTreeWrapperPass>();
371 AU.addRequired<TargetTransformInfoWrapperPass>();
372 }
373 AU.addPreserved<DominatorTreeWrapperPass>();
374 }
375
376 StringRef getPassName() const override {
377 return "Exception handling preparation";
378 }
379};
380
381} // end anonymous namespace
382
385 auto *DT = FAM.getCachedResult<DominatorTreeAnalysis>(F);
386 const TargetTransformInfo *TTI = nullptr;
387 auto OptLevel = TM->getOptLevel();
388
389 auto &MAMProxy = FAM.getResult<ModuleAnalysisManagerFunctionProxy>(F);
390
391 const LibcallLoweringModuleAnalysisResult *LibcallLowering =
392 MAMProxy.getCachedResult<LibcallLoweringModuleAnalysis>(*F.getParent());
393
394 if (!LibcallLowering) {
395 F.getContext().emitError("'" + LibcallLoweringModuleAnalysis::name() +
396 "' analysis required");
397 return PreservedAnalyses::all();
398 }
399
400 if (OptLevel != CodeGenOptLevel::None) {
401 if (!DT)
402 DT = &FAM.getResult<DominatorTreeAnalysis>(F);
403 TTI = &FAM.getResult<TargetIRAnalysis>(F);
404 }
405
406 const TargetSubtargetInfo *Subtarget = TM->getSubtargetImpl(F);
407 const LibcallLoweringInfo &Libcalls =
408 LibcallLowering->getLibcallLowering(*Subtarget);
409
410 bool Changed =
411 prepareDwarfEH(OptLevel, F, Libcalls, DT, TTI, TM->getTargetTriple());
412
413 if (!Changed)
414 return PreservedAnalyses::all();
417 return PA;
418}
419
420char DwarfEHPrepareLegacyPass::ID = 0;
421
422INITIALIZE_PASS_BEGIN(DwarfEHPrepareLegacyPass, DEBUG_TYPE,
423 "Prepare DWARF exceptions", false, false)
428INITIALIZE_PASS_END(DwarfEHPrepareLegacyPass, DEBUG_TYPE,
429 "Prepare DWARF exceptions", false, false)
430
432 return new DwarfEHPrepareLegacyPass(OptLevel);
433}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
This file implements the BitVector class.
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
This file contains the declarations for the subclasses of Constant, which represent the different fla...
static bool prepareDwarfEH(CodeGenOptLevel OptLevel, Function &F, const LibcallLoweringInfo &Libcalls, DominatorTree *DT, const TargetTransformInfo *TTI, const Triple &TargetTriple)
static bool runOnFunction(Function &F, bool PostInlining)
#define DEBUG_TYPE
Module.h This file contains the declarations for the Module class.
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
FunctionAnalysisManager FAM
#define INITIALIZE_PASS_DEPENDENCY(depName)
Definition PassSupport.h:42
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition PassSupport.h:44
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Definition PassSupport.h:39
This file defines the SmallVector class.
This file defines the 'Statistic' class, which is designed to be an easy way to expose various metric...
#define STATISTIC(VARNAME, DESC)
Definition Statistic.h:171
This file describes how to lower LLVM code to machine code.
Target-Independent Code Generator Pass Configuration Options pass.
This pass exposes codegen information to IR-level passes.
AnalysisUsage & addRequired()
AnalysisUsage & addPreserved()
Add the specified Pass class to the set of analyses preserved by this pass.
LLVM_ABI const LandingPadInst * getLandingPadInst() const
Return the landingpad instruction associated with the landing pad.
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
Definition BasicBlock.h:206
const Instruction * getTerminator() const LLVM_READONLY
Returns the terminator instruction if the block is well formed or null if the block is not well forme...
Definition BasicBlock.h:233
static BranchInst * Create(BasicBlock *IfTrue, InsertPosition InsertBefore=nullptr)
void setCallingConv(CallingConv::ID CC)
void setDoesNotReturn()
static CallInst * Create(FunctionType *Ty, Value *F, const Twine &NameStr="", InsertPosition InsertBefore=nullptr)
Analysis pass which computes a DominatorTree.
Definition Dominators.h:283
Legacy analysis pass which computes a DominatorTree.
Definition Dominators.h:321
Concrete subclass of DominatorTreeBase that is used to compute a normal dominator tree.
Definition Dominators.h:164
PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM)
static ExtractValueInst * Create(Value *Agg, ArrayRef< unsigned > Idxs, const Twine &NameStr="", InsertPosition InsertBefore=nullptr)
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:314
DISubprogram * getSubprogram() const
Get the attached subprogram.
DomTreeT & getDomTree()
Flush DomTree updates and return DomTree.
void applyUpdates(ArrayRef< UpdateT > Updates)
Submit updates to all available trees.
unsigned getNumIndices() const
idx_iterator idx_begin() const
LLVM_ABI InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
void setDebugLoc(DebugLoc Loc)
Set the debug location information for this instruction.
Tracks which library functions to use for a particular subtarget.
LLVM_ABI const char * getLibcallName(RTLIB::Libcall Call) const
Get the libcall routine name for the specified libcall.
LLVM_ABI CallingConv::ID getLibcallCallingConv(RTLIB::Libcall Call) const
Record a mapping from subtarget to LibcallLoweringInfo.
void addIncoming(Value *V, BasicBlock *BB)
Add an incoming value to the end of the PHI list.
static PHINode * Create(Type *Ty, unsigned NumReservedValues, const Twine &NameStr="", InsertPosition InsertBefore=nullptr)
Constructors - NumReservedValues is a hint for the number of incoming edges that this phi node will h...
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
PreservedAnalyses & preserve()
Mark an analysis as preserved.
Definition Analysis.h:132
Resume the propagation of an exception.
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
void push_back(const T &Elt)
Analysis pass providing the TargetTransformInfo.
const Triple & getTargetTriple() const
virtual const TargetSubtargetInfo * getSubtargetImpl(const Function &) const
Virtual method implemented by subclasses that returns a reference to that target's TargetSubtargetInf...
Target-Independent Code Generator Pass Configuration Options.
TargetSubtargetInfo - Generic base class for all target subtargets.
Wrapper pass for TargetTransformInfo.
This pass provides access to the codegen interfaces that are needed for IR-level transformations.
Triple - Helper class for working with autoconf configuration names.
Definition Triple.h:47
bool isTargetEHABICompatible() const
Tests whether the target supports the EHABI exception handling standard.
Definition Triple.h:955
Value * getOperand(unsigned i) const
Definition User.h:207
LLVM Value Representation.
Definition Value.h:75
bool use_empty() const
Definition Value.h:346
const ParentTy * getParent() const
Definition ilist_node.h:34
self_iterator getIterator()
Definition ilist_node.h:123
Changed
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition CallingConv.h:24
@ BasicBlock
Various leaf nodes.
Definition ISDOpcodes.h:81
PointerTypeMap run(const Module &M)
Compute the PointerTypeMap for the module M.
This is an optimization pass for GlobalISel generic memory operations.
Definition Types.h:26
FunctionAddr VTableAddr Value
Definition InstrProf.h:137
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
OuterAnalysisManagerProxy< ModuleAnalysisManager, Function > ModuleAnalysisManagerFunctionProxy
Provide the ModuleAnalysisManager to Function proxy.
bool isScopedEHPersonality(EHPersonality Pers)
Returns true if this personality uses scope-style EH IR instructions: catchswitch,...
LLVM_ABI EHPersonality classifyEHPersonality(const Value *Pers)
See if the given exception handling personality function is one that we understand.
LLVM_ABI FunctionPass * createDwarfEHPass(CodeGenOptLevel OptLevel)
createDwarfEHPass - This pass mulches exception handling code into a form adapted to code generation.
CodeGenOptLevel
Code generation optimization level.
Definition CodeGen.h:82
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
TargetTransformInfo TTI
LLVM_ABI bool simplifyCFG(BasicBlock *BB, const TargetTransformInfo &TTI, DomTreeUpdater *DTU=nullptr, const SimplifyCFGOptions &Options={}, ArrayRef< WeakVH > LoopHeaders={})
AnalysisManager< Function > FunctionAnalysisManager
Convenience typedef for the Function analysis manager.
LLVM_ABI bool isPotentiallyReachable(const Instruction *From, const Instruction *To, const SmallPtrSetImpl< BasicBlock * > *ExclusionSet=nullptr, const DominatorTree *DT=nullptr, const LoopInfo *LI=nullptr)
Determine whether instruction 'To' is reachable from 'From', without passing through any blocks in Ex...
Definition CFG.cpp:282