This commit adds caching for mutual reachability among children of a
node, in SimplifyInstanceAtOffset0.
This makes the DLAStep 10% to 50% faster on real-world benchmarks we
have measured, such as `updatedb.plocate` and `df` for Ubuntu 22.04
x86_64.
llvm::EquivalenceClasses is an efficient data structure from LLVM, to
compute equivalence classes among objects with Tarjan's union-find.
This commit uses that to avoid an hand-crafted very inefficient
algorithm.
Before this commit, the dla::Step CompactCompatibleArrays was
occasionally generating artificial nodes that were larger than the inner
data, forcing their size to Stride - AvailableSlack.
This commit fixes the problem keeping track of the actual size that is
consumed in the array element, and forcing the size of the artificial
nodes to that.
The dla::Step ArrangeAccessesHierarchically looks throught the DLA graph
and for each node with many outgoing instance edges tries to see if some
of them are hierachically contained within each other.
In order to do this, before this commit, we were building an auxiliary
graph representing this hierarchy, and in order to build it we had to
perform a number of comparisons between edges that was quadratic with
the number of edges.
Moreover, once all the comparisons were done, we had a deep graph
representing inclusion between edges, but we only cared about the
top-level of this graph, i.e. only the edges that contained other edges
hierarchically, but were not contained in other edges (we'll call them
the root edges).
So we were doing a quadratic number of comparisons but possibly many of
them were useless.
Finally, all the edges that were included in root edges, were pushed
down, but only for a single layer, because they needed to be re-compared
later with the children of the root edges they were being pushed
through. This latter part was responsible for a lot of wasted
computation that just needed to be done over and over at all the layers.
Overall this algorithm was doing a lot of wasted computation.
This commit replaces this logic with a new algorithm.
Now we keep track only of the root edges, and we compare only root edges
with other root edges.
Initially all edges are root edges.
Then we start comparing them.
If a root edge A is included in another one B, then A is not a root edge
anymore, and all the edges that were previously found to be included in
A are not included in B.
This algorithm still does a worst case of quadratic number of
comparisons, but drastically reduces the amount of useless computation
that is redone later. In particular:
- in cases where there are a lot of root edges (meaning that only a few,
or no edge can be included in others) we do a number of comparison
close to quadratic, but we're only pushing non-root edges down, so
we'll never have to redo comparisons in deeper layers
- in cases where there is only a small number of root edges, we're doing
a number of comparisons close to linear, and we never compare non-root
edges with each other, so we're saving a lot of computation that would
be wasted (because it would need to be redone in deeper layers).
Before this commit we were manually building a topological ordering,
that resulted in worse overall performance in some scenarios.
This commit changes the dla::Step to temporarily introduce a fake root
node, and computes a RPOT from there, which overall yields better
results.
Before this commit, a logic mistake made the AvailableSlack too large
for arrays that started near the beginning of their parent.
This in turn allowed a compacted array to "underflow" on lower addresses
w.r.t. the parent.
This commit fixes the problem by properly computing the AvailableSlack,
restraining it in cases where the array under analysis is close to the
beginning of the parent.
This commit enables ArrangeAccessesHierachically to push down pointer
edges when rearranging accesses hierarchically.
In particular, if an edge N is being pushed down another edge M, and M
is at offset 0, and N reaches a pointer edge PE that points to a
grandparent G of M at offset 0, then we want to push down the pointer
edge PE so that it points to the target of M, unless there is another
edge M' different from M, such that M' has the same properties of M but
target(M) != target(M').
Before this commit, ArrangeAccesseHierachically was removing nodes from
the graph, instead of merging them.
This degraded the information collected on the graph useful for debug.
This commit drops the use of removeNode method, and switches to using
mergeNodes, that preserves the useful debug information when merging.
Before this commit postProcessMerge was making some strong assumptions
about dla::CollapseSingleChild::collapseSingle that no longer hold.
Namely the (now) wrong assumption is that collapseSingle would not break
post_order iteration, which is now false because collapseSingle can
change the incoming edges of the node it's called on, and those incoming
edges are on the visit stack of the post_order iteration itself.
This required making a copy of the post order, and resulted in dropping
the postProcessMerge function alltogether.
Before this commit we were using int64_t for Offset, Strides, and
TripCounts.
Originally, this choice was intended because for some time we envisioned
actually having a use for negative values, but in the end we decided
there's no use for those.
This commit switches all to unsigned integers, allowing to remove some
static_cast across the codebase, and overall easing typicall computation
we have to perform on those fields.
A logic bug caused the wrong detection of nodes with many fields.
As a result, the field deduplication was only executed on a subset of
the real candidates.
This caused the following two problems.
- Degradation of the quality of the results, i.e. unions that could be
deduplicated were not deduplicated.
- Failing assertions in rare cases.
The algorithm assumes that if it's looking at the children
(C1, ..., Cn) of node A to be deduplicated, then all (C1, ..., Cn)
have beed already visited, hence they don't have children that should
be duplicated because they would have been deduplicated already, when
looking at (C1, ..., Cn). The bug possibly caused to miss the
deduplication of children of Ci (for some i) causing assertions to
fail when looking at A to deduplicate (C1, ..., Cn).
Before this commit, the dla::Step depended on InterferingInfo, but this
was superfluous.
This commit drops the dependency and enables the Step to run before
ComputeNonInterferingComponents.
This also avoids the need to run ComputeNonInterferingComponents twice
(one before and one after DeduplicateUnionFields), so it can only run
once.
Before this commit, CollapseSingleChild Step was thinkering around
with InterferingInfo.
Now CollapseSingleChild does not care about InterferingInfo anymore, so
the code handling it can just be dropped.
The step now collapse parents with their single child if they are
indistinguishable, i.e. if the parent has only that single child, at
offset zero, and their size is the same.
This pass was never implemented and we don't have plans to do it soon.
This commit just drops it to reduce the noise.
Whenever we decide to implement something similar we'll do it from
scratch.
Change company name to "rev.ng Labs Srl" in all license headers
to reflect changed company name and legal status
Add missing license headers to files that didn't have one
Before this commit, each dla::Step handled the printing of its own .dot
files for debug.
This commit moves the logic for dumping the .dot files into the main
loop of dla::StepManager, guarding it with a single Logger.
Before this commit, the dla::Step RemoveInvalidStrideEdges was not
taking into account that dropping edges could change the size of the
node where the edges originated from.
This commit fixes the problem, a) recomputing the size automatically
when needed, and b) adding proper dependencies in the dla middleend
pipeline, so that these changes are properly propagated updwards and
don't break any pre-conditions of following dla::Steps.
This step removes redundant instance-at-offset-0, collapsing the child
into the parent, whenever this operation does not induce instance-loops
on the graph.
This is intended to reduce the number of shallow wrapper structs and
unions.