[PATCH] D13274: [WinEH] Clone funclets with multiple parents
Joseph Tremoulet via llvm-commits
llvm-commits at lists.llvm.org
Thu Oct 1 13:48:13 PDT 2015
JosephTremoulet added a comment.
Thanks for taking this on. I think the logic for cleanupret/cleanupendpad needs to be a little different, and that `makeFuncletEdgeUnreachable` can defer to `removeUnwindEdge`; aside from that, just a few nits.
================
Comment at: lib/CodeGen/WinEHPrepare.cpp:2980-2987
@@ +2979,10 @@
+ for (BasicBlock *Parent : ColorMapItem) {
+ assert(std::find(FuncletChildren[Parent].begin(),
+ FuncletChildren[Parent].end(),
+ FuncletEntry) == std::end(FuncletChildren[Parent]));
+ FuncletChildren[Parent].push_back(FuncletEntry);
+ assert(std::find(FuncletParents[FuncletEntry].begin(),
+ FuncletParents[FuncletEntry].end(),
+ Parent) == std::end(FuncletParents[FuncletEntry]));
+ FuncletParents[FuncletEntry].push_back(Parent);
+ }
----------------
Looks like you found an elegant way to determinize the output. Cool.
================
Comment at: lib/CodeGen/WinEHPrepare.cpp:2997-2998
@@ +2996,4 @@
+// original funclet are remapped to unwind to the clone. Any terminator in the
+// original funclet which returned to this parent is converted to an unreachable
+// to an unreachable instruction. Likewise, any terminator in the cloned funclet
+// which returns to a parent funclet other than the specified parent is
----------------
typo: "to an unreachable to an unreachable"
================
Comment at: lib/CodeGen/WinEHPrepare.cpp:3039-3053
@@ +3038,17 @@
+ }
+ } else if (auto *CRI = dyn_cast<CleanupReturnInst>(Terminator)) {
+ // If we're cloning a cleanup pad and the uncloned cleanup pad unwinds
+ // to a block in the clone parent, we need to replace the cleanupret with
+ // an unreachable instruction.
+ BasicBlock *UnwindDest = CRI->getUnwindDest();
+ if (UnwindDest && FuncletBlocks[Parent].count(UnwindDest)) {
+ CRI->eraseFromParent();
+ new UnreachableInst(BB->getContext(), BB);
+ // Loop over all of the successors, removing BB's entry from any PHI
+ // nodes.
+ for (succ_iterator SI = succ_begin(BB), SE = succ_end(BB); SI != SE;
+ ++SI)
+ (*SI)->removePredecessor(BB);
+ }
+ }
+ }
----------------
Cleanupret targets a sibling, not a parent. So,
1. I don't think this code should be here.
2. I guess you should have code somewhere that updates these sibling->sibling edges, though you probably can't do that until both siblings have been cloned...
Also, whatever you do about this with cleanupret, I think cleanupendpad should get the same treatment
================
Comment at: lib/CodeGen/WinEHPrepare.cpp:3087-3100
@@ +3086,16 @@
+ }
+ } else if (auto *CRI = dyn_cast<CleanupReturnInst>(Terminator)) {
+ // If we're cloning a cleanup pad and the cloned cleanup pad does not
+ // unwind to a block in the clone parent, we need to replace the
+ // cleanupret with an unreachable instruction.
+ BasicBlock *UnwindDest = CRI->getUnwindDest();
+ if (UnwindDest && !FuncletBlocks[Parent].count(UnwindDest)) {
+ CRI->eraseFromParent();
+ new UnreachableInst(NewBlock->getContext(), NewBlock);
+ // Loop over all of the successors, removing NewBlock's entry from any
+ // PHI nodes.
+ for (succ_iterator SI = succ_begin(NewBlock), SE = succ_end(NewBlock);
+ SI != SE; ++SI)
+ (*SI)->removePredecessor(NewBlock);
+ }
+ }
----------------
As above, cleanupret should target a sibling, so I don't think this is right.
================
Comment at: lib/CodeGen/WinEHPrepare.cpp:3168-3170
@@ +3167,5 @@
+ // This asserts a condition that is relied upon inside the loop below.
+ for (auto *Pred : predecessors(FuncletEntry))
+ assert(std::find(pred_begin(ClonedFunclet), pred_end(ClonedFunclet),
+ Pred) == pred_end(ClonedFunclet));
+
----------------
nit: you could use `std::all_of` to get this whole thing under the assert (or wrap it in `#ifndef NDEBUG`)
================
Comment at: lib/CodeGen/WinEHPrepare.cpp:3206-3208
@@ +3205,5 @@
+// previously unwound to the specified child funclet so that the unwind
+// path is unreachable. In the case of invoke instructions which unwound to
+// the child funclet, a cleanup pad with an unreachable instruction as its
+// body is created and used as the unwind destination.
+void WinEHPrepare::makeFuncletEdgeUnreachable(BasicBlock *Parent,
----------------
Why not just transform such invokes into calls?
================
Comment at: lib/CodeGen/WinEHPrepare.cpp:3217-3219
@@ +3216,5 @@
+ // An invoke that unwinds to the child funclet must be handled as a
+ // special case. Simply converting the invoke to a call would leave
+ // the behavior in an ambiguous state. We want the unwind edge to
+ // actually be flagged as unreachable.
+ //
----------------
I don't follow this logic. If simplifycfg determines an invoke's EH pad to be unreachable, it changes it to a call, and I don't see why we couldn't do the same here. It would be nice in both cases to mark the call as !MayThrow, but that can/should be a separate change.
I think the argument goes: since the unwind edge is unreachable, that must mean that no exception escapes the call/invoke, so modeling it as a call (that will happen not to throw) is sound.
================
Comment at: lib/CodeGen/WinEHPrepare.cpp:3250-3251
@@ +3249,4 @@
+ UnwindDest = I->getUnwindDest();
+ else if (auto *I = dyn_cast<CleanupEndPadInst>(Terminator))
+ UnwindDest = I->getUnwindDest();
+ else if (auto *I = dyn_cast<TerminatePadInst>(Terminator))
----------------
CleanupEndPad transfers to sibling, same as CleanupRet
================
Comment at: lib/CodeGen/WinEHPrepare.cpp:3260-3261
@@ +3259,4 @@
+ if (UnwindDest == Child) {
+ Terminator->eraseFromParent();
+ new UnreachableInst(BB->getContext(), BB);
+ }
----------------
In the case that the terminator is a catchendpad or terminatepad, this has the same problem you called out above that an unreachable can't be an EH pad.
I think you could remove the special-casing for invoke (so add a case for it in the if/else ladder down here), and if it's a terminator whose unwind edge goes to the child and needs to be removed, call `llvm::removeUnwindEdge(BB)`.
================
Comment at: lib/CodeGen/WinEHPrepare.cpp:3292-3294
@@ +3291,5 @@
+ bool IsCyclic = false;
+ BasicBlock *ChildIdentity = IdentityMap[ChildFunclet];
+ if (!ChildIdentity)
+ IdentityMap[ChildFunclet] = ChildIdentity = ChildFunclet;
+ for (BasicBlock *Ancestor : FuncletPath) {
----------------
FWIW, I had no idea what `IdentityMap` would be from its name. I did find `Orig2Clone` above intuitive, so maybe something like `OriginalMap` might be better?
================
Comment at: lib/CodeGen/WinEHPrepare.cpp:3297-3298
@@ +3296,4 @@
+ BasicBlock *AncestorIdentity = IdentityMap[Ancestor];
+ if (!AncestorIdentity)
+ IdentityMap[Ancestor] = AncestorIdentity = Ancestor;
+ if (AncestorIdentity == ChildIdentity) {
----------------
I'm pretty sure you'd never hit this case if you pre-seeded `IdentityMap` to map the function entry block to itself.
================
Comment at: lib/CodeGen/WinEHPrepare.cpp:3318
@@ +3317,3 @@
+ }
+}
+
----------------
I think probably right here after the loop mapping children ends is the right place to fix up cleanupret/cleanupendpad (sibling) edges.
Repository:
rL LLVM
http://reviews.llvm.org/D13274
More information about the llvm-commits
mailing list