Previously we represented all successors in a list where each successor
was represented by a SuccessorID. A SuccessorID is essentially an
unsigned int that stores some flags in the lower bits of the
integer. The two flags are: 1. IsDead, 2. IsNonLocal (where non local
means that it is a loop exit edge). When the IsNonLocal flag is not set,
the integer that is actually stored represents the ID of the successor
region. If the IsNonLocal flag is set, then the integer represents the
index in the parent region's successor list of the actual loop exit
successor.
The bug I mentioned was that we were not being careful enough in the
replacement code to distinguish in between successors that were loop
exit successors and those that were not. This could cause us to replace
a loop exit successor whose integer value was the same as the index of a
non-loop exit successor with the non-loop exit successor. This then
would cause us to miscompile and or introduce duplicate values into the
successor list. Luckily an assert I added caught the latter condition.
After I fixed this problem, an interesting performance issue was
exposed. I had assumed when using a list that I would never have more
than 10-20 successors (in general a reasonable assumption). But
introducing loop exit successors adds in an interesting wrinkle, namely
every block with an unreachable terminator will result in a successor
edge. This makes just iterating over a uniqued list really slow.
I solved the issue by writing a new data structure called
BlotSetVector. The interesting thing about BlotSetVector is that all
operations preserve index offsets. This means that if one erases a
value, all other values in the set vector are not moved around in the
internal vector. This is important since a loop exit edge needs to refer
to an invariant offset in the parent region's successor array since we
do not want to have to go through and update large amounts of unrelated
edges every time we erase an edge.
In terms of a test case, this invariant was impossible for me to reproduce since
it is sensitive to the order of successors. Even dumping the file and running
the analysis would not catch it. After 2 days of trying to make a test case I
gave up. But I filed rdar://23228299 to verify that sil-opt can round trip
in memory ordering of various items such as use lists, successors, predecessors,
block ordering, function ordering, etc.
rdar://22238658
Swift SVN r32839
Hubris is my name. = /. I put in a verifier check to make sure that we can
properly look up some value and if we have two blocks related via a non-local
successor chain, the first is a CFG level successor of the second.
Swift SVN r32759
I have been able to run LoopRegionAnalysis in the normal pileline and compile
the stdlib so I think this may not be /needed/, but why not save some compile
time and potentially avoid future correctness errors.
Swift SVN r32755
This method on LoopRegionFunctionInfo given a Region and an initial
parent successor index, looks up through the loop nest hierarchy to find
the region associated with the non-local successor.
rdar://22238658
Swift SVN r32754
Ignoring regions, define a non-local successor of a basic block BB as a
successor of BB that is not in the same parent loop. In terms of the
current SIL loop analysis a non-local successor is referred to as an
"exit block" and BB as an "exiting block".
To transform that statement into the world of regions, we say that a
non-local successor of a region R is a region S that when we are
constructing the parent region of R (which we will call P(R)) has the
property that P(S) != P(R). (This is using the construction algorithm of
the loop region data structure to define it, one could also create
analogous definitions per block and loop, but I am going to leave that
to an in commit comment).
The main issue with non-local successors is that if we were to represent
them directly we would break the invariant that regions only have edges
that point at regions in side the same parent region* or at the parent region
itself.
In order to get around this issue, we break each of the non-local
successor edges into smaller non-local successor edges that point up
from a region to its parent. Then given the region, one can climb up
non-local successor edges into parent regions until one finds the real
non-local successor which the final top level parent region will have as
a local successor.
This is important in order to be able to handle trap basic blocks in the ARC
optimizer and early exits more generally.
* Consider an early exit from an inner loop. The early exit block will
be a successor of the outer loop. The basic block in the inner loop
will then have the early exit block as a non-local successor.
rdar://22238658
Swift SVN r32695
In a forthcoming patch I am going to add support to loop regions to
support non-local successors. This patch is an incremental step that
does not change the current output.
Without getting into too many details (see the forthcoming patch), The
way that I am going to implement this is by creating a tagged
SuccessorID that consists either of an index into the successor list of
the parent node or the index of a local successor. Since the
SuccessorIDs will be referring to the parent regions successor list, we
can not sort or remove successors from the successor list since it would
invalidate such indices. We still maintain the property that no
successor edges are represented twice (the real property that we wanted
after all), but by performing the check at insertion time instead of
just sorting at various places.
This patch begins the transition down that path by:
1. Creating a new struct SuccessorID with an IsDead flag on it.
2. Never sorting or removing SuccessorIDs from the list. Instead we just
mark them dead and use FilterRanges to ignore them.
3. Adding a verifier that verifies that our successor lists do not have
any duplicate elements. This ensures that we maintain the
no-duplicate property.
rdar://22238658
Swift SVN r32694
When we are printing out the subregions/predecessors/successors of a region, we
just need the ID. This is just a cleanup to make the tests easier to read.
Swift SVN r32648
Mark and I are trying to canonicalize on referring to Blocks instead of BB when
possible. This is just an incremental change towards that.
Swift SVN r32633
This analysis provides the following functionality:
1. An abstraction over all loops and basic blocks called a 'region'. For
a given loop this abstraction is used to represent all immediate child
basic blocks (1) and immediate subloops (2) called child regions.
2. Detection of back edges that were not found by SILLoopInfo. The
heads/tails of such back edges are marked as being areas where unhandled
control flow occurs. This enables irreducible control flow to be handled
by users as appropriate.
3. For each loop in the loop tree an RPO/PO ordering of the loop's
subregions. This enables one to run dataflow on the loop tree using loop
abstractions in a structured, clean manner by processing regions and
depending on whether or not the region was a loop/BB handle it
appropriately.
Some notes:
1. Functions are represented as a top level loop.
2. The analysis is setup to conservatively handle loops with the
following properties:
a. Irreducible Control Flow.
b. Multiple Backedges
In these cases, we use the unknown control flow facility to allow for
users of the analysis to handle the behavior appropriately.
We assume 2.b. since I am going to implement canonicalization of
multiple backedge loops into nested loops.
Also two passes are provided for working with this analysis:
1. LoopRegionViewText - Dump loop regions in a textual format. This is
intended for usage with FileCheck tests.
2. LoopRegionViewCFG - Dump loop regions into a CFG pdf. The dumped CFG
includes the loops, functions, and BBs. It represents
successors/predecessors in the loop region by black arrows and
hierarchical relations in between regions/subregions by red dashed
arrows. It also prints out the contents of BBs and whether or not
unknown control flow occurs.
(1) BBs for which this loop is the inner most parent loop.
(2) Loops for which this loop is the inner most parent loop.
Thanks to Arnold review!
rdar://22238531
Swift SVN r32104