First let me comment by saying I'm not sure if the community desires to go in this direction. The primary reason for uploading this patch was twofold:
- To start a discussion about how we work with edges between blocks in an IR vs CFG manner.
- To archive the patch should others find the idea useful in the future.
This patch adds a new function removeEdge() as a BasicBlock primitive. It does exactly what the name implies: removes an outgoing edge from BB1 into BB2. The routine asserts if no such edge exists.
This routine started as a reaction to removePredecessor(); a call that only adjust PHIs in the successor BB. Calls to removePredecessor() quietly do nothing in cases such as the following:
BB1:
br BB2
BB2:
br BB3
BB2->removePredecessor(BB1)
This is a bit surprising until one reads the documentation/source for removePredecessor().
The removeEdge() routine alters the terminator instruction of BB1. Should we call BB1->removeEdge(BB2) the resulting IR would now look like
BB1:
unreachable
BB2:
br BB3
Dominance is preserved if a DT handle is passed in: BB1->removeEdge(BB2, DT).
This new interface gives the caller the option to think more about the CFG than the underlying IR details. This has a few complications related to it:
- The successors of BB1 will be altered by the call, making looping over successors different than of call sites using removePredecessor()
- removeEdge() does not alter uses.
Even with these drawbacks I think this alternative interface has merit: in too many locations throughout the middle end do we see special-cased code that deals with the details of the IRs many terminator instruction types. At the very least I hope this code spurs a fruitful discussion.
Fixed in rev 2, needed to check NormalDestBB != UnwindDestBB in the corner-case where Normal and Unwind are the same basic block.