1f65dcba8SDimitry Andric //===- SampleProfileInference.cpp - Adjust sample profiles in the IR ------===//
2f65dcba8SDimitry Andric //
3f65dcba8SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4f65dcba8SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5f65dcba8SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6f65dcba8SDimitry Andric //
7f65dcba8SDimitry Andric //===----------------------------------------------------------------------===//
8f65dcba8SDimitry Andric //
9f65dcba8SDimitry Andric // This file implements a profile inference algorithm. Given an incomplete and
10f65dcba8SDimitry Andric // possibly imprecise block counts, the algorithm reconstructs realistic block
11f65dcba8SDimitry Andric // and edge counts that satisfy flow conservation rules, while minimally modify
12f65dcba8SDimitry Andric // input block counts.
13f65dcba8SDimitry Andric //
14f65dcba8SDimitry Andric //===----------------------------------------------------------------------===//
15f65dcba8SDimitry Andric
16f65dcba8SDimitry Andric #include "llvm/Transforms/Utils/SampleProfileInference.h"
176f8fc217SDimitry Andric #include "llvm/ADT/BitVector.h"
18145449b1SDimitry Andric #include "llvm/Support/CommandLine.h"
19f65dcba8SDimitry Andric #include "llvm/Support/Debug.h"
20f65dcba8SDimitry Andric #include <queue>
21f65dcba8SDimitry Andric #include <set>
22145449b1SDimitry Andric #include <stack>
237fa27ce4SDimitry Andric #include <unordered_set>
24f65dcba8SDimitry Andric
25f65dcba8SDimitry Andric using namespace llvm;
26f65dcba8SDimitry Andric #define DEBUG_TYPE "sample-profile-inference"
27f65dcba8SDimitry Andric
28f65dcba8SDimitry Andric namespace {
29f65dcba8SDimitry Andric
30e3b55780SDimitry Andric static cl::opt<bool> SampleProfileEvenFlowDistribution(
31e3b55780SDimitry Andric "sample-profile-even-flow-distribution", cl::init(true), cl::Hidden,
32e3b55780SDimitry Andric cl::desc("Try to evenly distribute flow when there are multiple equally "
33145449b1SDimitry Andric "likely options."));
34145449b1SDimitry Andric
35e3b55780SDimitry Andric static cl::opt<bool> SampleProfileRebalanceUnknown(
36e3b55780SDimitry Andric "sample-profile-rebalance-unknown", cl::init(true), cl::Hidden,
37e3b55780SDimitry Andric cl::desc("Evenly re-distribute flow among unknown subgraphs."));
38145449b1SDimitry Andric
39e3b55780SDimitry Andric static cl::opt<bool> SampleProfileJoinIslands(
40e3b55780SDimitry Andric "sample-profile-join-islands", cl::init(true), cl::Hidden,
41e3b55780SDimitry Andric cl::desc("Join isolated components having positive flow."));
42145449b1SDimitry Andric
43e3b55780SDimitry Andric static cl::opt<unsigned> SampleProfileProfiCostBlockInc(
44e3b55780SDimitry Andric "sample-profile-profi-cost-block-inc", cl::init(10), cl::Hidden,
45e3b55780SDimitry Andric cl::desc("The cost of increasing a block's count by one."));
46145449b1SDimitry Andric
47e3b55780SDimitry Andric static cl::opt<unsigned> SampleProfileProfiCostBlockDec(
48e3b55780SDimitry Andric "sample-profile-profi-cost-block-dec", cl::init(20), cl::Hidden,
49e3b55780SDimitry Andric cl::desc("The cost of decreasing a block's count by one."));
50145449b1SDimitry Andric
51e3b55780SDimitry Andric static cl::opt<unsigned> SampleProfileProfiCostBlockEntryInc(
52e3b55780SDimitry Andric "sample-profile-profi-cost-block-entry-inc", cl::init(40), cl::Hidden,
53e3b55780SDimitry Andric cl::desc("The cost of increasing the entry block's count by one."));
54145449b1SDimitry Andric
55e3b55780SDimitry Andric static cl::opt<unsigned> SampleProfileProfiCostBlockEntryDec(
56e3b55780SDimitry Andric "sample-profile-profi-cost-block-entry-dec", cl::init(10), cl::Hidden,
57e3b55780SDimitry Andric cl::desc("The cost of decreasing the entry block's count by one."));
58e3b55780SDimitry Andric
59e3b55780SDimitry Andric static cl::opt<unsigned> SampleProfileProfiCostBlockZeroInc(
60e3b55780SDimitry Andric "sample-profile-profi-cost-block-zero-inc", cl::init(11), cl::Hidden,
61e3b55780SDimitry Andric cl::desc("The cost of increasing a count of zero-weight block by one."));
62e3b55780SDimitry Andric
63e3b55780SDimitry Andric static cl::opt<unsigned> SampleProfileProfiCostBlockUnknownInc(
64e3b55780SDimitry Andric "sample-profile-profi-cost-block-unknown-inc", cl::init(0), cl::Hidden,
65e3b55780SDimitry Andric cl::desc("The cost of increasing an unknown block's count by one."));
66145449b1SDimitry Andric
67f65dcba8SDimitry Andric /// A value indicating an infinite flow/capacity/weight of a block/edge.
68f65dcba8SDimitry Andric /// Not using numeric_limits<int64_t>::max(), as the values can be summed up
69f65dcba8SDimitry Andric /// during the execution.
70f65dcba8SDimitry Andric static constexpr int64_t INF = ((int64_t)1) << 50;
71f65dcba8SDimitry Andric
72f65dcba8SDimitry Andric /// The minimum-cost maximum flow algorithm.
73f65dcba8SDimitry Andric ///
74f65dcba8SDimitry Andric /// The algorithm finds the maximum flow of minimum cost on a given (directed)
75f65dcba8SDimitry Andric /// network using a modified version of the classical Moore-Bellman-Ford
76f65dcba8SDimitry Andric /// approach. The algorithm applies a number of augmentation iterations in which
77f65dcba8SDimitry Andric /// flow is sent along paths of positive capacity from the source to the sink.
78f65dcba8SDimitry Andric /// The worst-case time complexity of the implementation is O(v(f)*m*n), where
79f65dcba8SDimitry Andric /// where m is the number of edges, n is the number of vertices, and v(f) is the
80f65dcba8SDimitry Andric /// value of the maximum flow. However, the observed running time on typical
81f65dcba8SDimitry Andric /// instances is sub-quadratic, that is, o(n^2).
82f65dcba8SDimitry Andric ///
83f65dcba8SDimitry Andric /// The input is a set of edges with specified costs and capacities, and a pair
84f65dcba8SDimitry Andric /// of nodes (source and sink). The output is the flow along each edge of the
85f65dcba8SDimitry Andric /// minimum total cost respecting the given edge capacities.
86f65dcba8SDimitry Andric class MinCostMaxFlow {
87f65dcba8SDimitry Andric public:
MinCostMaxFlow(const ProfiParams & Params)88e3b55780SDimitry Andric MinCostMaxFlow(const ProfiParams &Params) : Params(Params) {}
89e3b55780SDimitry Andric
90f65dcba8SDimitry Andric // Initialize algorithm's data structures for a network of a given size.
initialize(uint64_t NodeCount,uint64_t SourceNode,uint64_t SinkNode)91f65dcba8SDimitry Andric void initialize(uint64_t NodeCount, uint64_t SourceNode, uint64_t SinkNode) {
92f65dcba8SDimitry Andric Source = SourceNode;
93f65dcba8SDimitry Andric Target = SinkNode;
94f65dcba8SDimitry Andric
95f65dcba8SDimitry Andric Nodes = std::vector<Node>(NodeCount);
96f65dcba8SDimitry Andric Edges = std::vector<std::vector<Edge>>(NodeCount, std::vector<Edge>());
97e3b55780SDimitry Andric if (Params.EvenFlowDistribution)
98145449b1SDimitry Andric AugmentingEdges =
99145449b1SDimitry Andric std::vector<std::vector<Edge *>>(NodeCount, std::vector<Edge *>());
100f65dcba8SDimitry Andric }
101f65dcba8SDimitry Andric
102f65dcba8SDimitry Andric // Run the algorithm.
run()103f65dcba8SDimitry Andric int64_t run() {
104e3b55780SDimitry Andric LLVM_DEBUG(dbgs() << "Starting profi for " << Nodes.size() << " nodes\n");
105e3b55780SDimitry Andric
106145449b1SDimitry Andric // Iteratively find an augmentation path/dag in the network and send the
107145449b1SDimitry Andric // flow along its edges
108145449b1SDimitry Andric size_t AugmentationIters = applyFlowAugmentation();
109f65dcba8SDimitry Andric
110f65dcba8SDimitry Andric // Compute the total flow and its cost
111f65dcba8SDimitry Andric int64_t TotalCost = 0;
112f65dcba8SDimitry Andric int64_t TotalFlow = 0;
113f65dcba8SDimitry Andric for (uint64_t Src = 0; Src < Nodes.size(); Src++) {
114f65dcba8SDimitry Andric for (auto &Edge : Edges[Src]) {
115f65dcba8SDimitry Andric if (Edge.Flow > 0) {
116f65dcba8SDimitry Andric TotalCost += Edge.Cost * Edge.Flow;
117f65dcba8SDimitry Andric if (Src == Source)
118f65dcba8SDimitry Andric TotalFlow += Edge.Flow;
119f65dcba8SDimitry Andric }
120f65dcba8SDimitry Andric }
121f65dcba8SDimitry Andric }
122f65dcba8SDimitry Andric LLVM_DEBUG(dbgs() << "Completed profi after " << AugmentationIters
123f65dcba8SDimitry Andric << " iterations with " << TotalFlow << " total flow"
124f65dcba8SDimitry Andric << " of " << TotalCost << " cost\n");
125f65dcba8SDimitry Andric (void)TotalFlow;
126145449b1SDimitry Andric (void)AugmentationIters;
127f65dcba8SDimitry Andric return TotalCost;
128f65dcba8SDimitry Andric }
129f65dcba8SDimitry Andric
130f65dcba8SDimitry Andric /// Adding an edge to the network with a specified capacity and a cost.
131f65dcba8SDimitry Andric /// Multiple edges between a pair of nodes are allowed but self-edges
132f65dcba8SDimitry Andric /// are not supported.
addEdge(uint64_t Src,uint64_t Dst,int64_t Capacity,int64_t Cost)133f65dcba8SDimitry Andric void addEdge(uint64_t Src, uint64_t Dst, int64_t Capacity, int64_t Cost) {
134f65dcba8SDimitry Andric assert(Capacity > 0 && "adding an edge of zero capacity");
135f65dcba8SDimitry Andric assert(Src != Dst && "loop edge are not supported");
136f65dcba8SDimitry Andric
137f65dcba8SDimitry Andric Edge SrcEdge;
138f65dcba8SDimitry Andric SrcEdge.Dst = Dst;
139f65dcba8SDimitry Andric SrcEdge.Cost = Cost;
140f65dcba8SDimitry Andric SrcEdge.Capacity = Capacity;
141f65dcba8SDimitry Andric SrcEdge.Flow = 0;
142f65dcba8SDimitry Andric SrcEdge.RevEdgeIndex = Edges[Dst].size();
143f65dcba8SDimitry Andric
144f65dcba8SDimitry Andric Edge DstEdge;
145f65dcba8SDimitry Andric DstEdge.Dst = Src;
146f65dcba8SDimitry Andric DstEdge.Cost = -Cost;
147f65dcba8SDimitry Andric DstEdge.Capacity = 0;
148f65dcba8SDimitry Andric DstEdge.Flow = 0;
149f65dcba8SDimitry Andric DstEdge.RevEdgeIndex = Edges[Src].size();
150f65dcba8SDimitry Andric
151f65dcba8SDimitry Andric Edges[Src].push_back(SrcEdge);
152f65dcba8SDimitry Andric Edges[Dst].push_back(DstEdge);
153f65dcba8SDimitry Andric }
154f65dcba8SDimitry Andric
155f65dcba8SDimitry Andric /// Adding an edge to the network of infinite capacity and a given cost.
addEdge(uint64_t Src,uint64_t Dst,int64_t Cost)156f65dcba8SDimitry Andric void addEdge(uint64_t Src, uint64_t Dst, int64_t Cost) {
157f65dcba8SDimitry Andric addEdge(Src, Dst, INF, Cost);
158f65dcba8SDimitry Andric }
159f65dcba8SDimitry Andric
160f65dcba8SDimitry Andric /// Get the total flow from a given source node.
161f65dcba8SDimitry Andric /// Returns a list of pairs (target node, amount of flow to the target).
getFlow(uint64_t Src) const162b1c73532SDimitry Andric std::vector<std::pair<uint64_t, int64_t>> getFlow(uint64_t Src) const {
163f65dcba8SDimitry Andric std::vector<std::pair<uint64_t, int64_t>> Flow;
164e3b55780SDimitry Andric for (const auto &Edge : Edges[Src]) {
165f65dcba8SDimitry Andric if (Edge.Flow > 0)
166f65dcba8SDimitry Andric Flow.push_back(std::make_pair(Edge.Dst, Edge.Flow));
167f65dcba8SDimitry Andric }
168f65dcba8SDimitry Andric return Flow;
169f65dcba8SDimitry Andric }
170f65dcba8SDimitry Andric
171f65dcba8SDimitry Andric /// Get the total flow between a pair of nodes.
getFlow(uint64_t Src,uint64_t Dst) const172f65dcba8SDimitry Andric int64_t getFlow(uint64_t Src, uint64_t Dst) const {
173f65dcba8SDimitry Andric int64_t Flow = 0;
174e3b55780SDimitry Andric for (const auto &Edge : Edges[Src]) {
175f65dcba8SDimitry Andric if (Edge.Dst == Dst) {
176f65dcba8SDimitry Andric Flow += Edge.Flow;
177f65dcba8SDimitry Andric }
178f65dcba8SDimitry Andric }
179f65dcba8SDimitry Andric return Flow;
180f65dcba8SDimitry Andric }
181f65dcba8SDimitry Andric
182f65dcba8SDimitry Andric private:
183145449b1SDimitry Andric /// Iteratively find an augmentation path/dag in the network and send the
184145449b1SDimitry Andric /// flow along its edges. The method returns the number of applied iterations.
applyFlowAugmentation()185145449b1SDimitry Andric size_t applyFlowAugmentation() {
186145449b1SDimitry Andric size_t AugmentationIters = 0;
187145449b1SDimitry Andric while (findAugmentingPath()) {
188145449b1SDimitry Andric uint64_t PathCapacity = computeAugmentingPathCapacity();
189145449b1SDimitry Andric while (PathCapacity > 0) {
190145449b1SDimitry Andric bool Progress = false;
191e3b55780SDimitry Andric if (Params.EvenFlowDistribution) {
192145449b1SDimitry Andric // Identify node/edge candidates for augmentation
193145449b1SDimitry Andric identifyShortestEdges(PathCapacity);
194145449b1SDimitry Andric
195145449b1SDimitry Andric // Find an augmenting DAG
196145449b1SDimitry Andric auto AugmentingOrder = findAugmentingDAG();
197145449b1SDimitry Andric
198145449b1SDimitry Andric // Apply the DAG augmentation
199145449b1SDimitry Andric Progress = augmentFlowAlongDAG(AugmentingOrder);
200145449b1SDimitry Andric PathCapacity = computeAugmentingPathCapacity();
201145449b1SDimitry Andric }
202145449b1SDimitry Andric
203145449b1SDimitry Andric if (!Progress) {
204145449b1SDimitry Andric augmentFlowAlongPath(PathCapacity);
205145449b1SDimitry Andric PathCapacity = 0;
206145449b1SDimitry Andric }
207145449b1SDimitry Andric
208145449b1SDimitry Andric AugmentationIters++;
209145449b1SDimitry Andric }
210145449b1SDimitry Andric }
211145449b1SDimitry Andric return AugmentationIters;
212145449b1SDimitry Andric }
213145449b1SDimitry Andric
214145449b1SDimitry Andric /// Compute the capacity of the cannonical augmenting path. If the path is
215145449b1SDimitry Andric /// saturated (that is, no flow can be sent along the path), then return 0.
computeAugmentingPathCapacity()216145449b1SDimitry Andric uint64_t computeAugmentingPathCapacity() {
217145449b1SDimitry Andric uint64_t PathCapacity = INF;
218145449b1SDimitry Andric uint64_t Now = Target;
219145449b1SDimitry Andric while (Now != Source) {
220145449b1SDimitry Andric uint64_t Pred = Nodes[Now].ParentNode;
221145449b1SDimitry Andric auto &Edge = Edges[Pred][Nodes[Now].ParentEdgeIndex];
222145449b1SDimitry Andric
223145449b1SDimitry Andric assert(Edge.Capacity >= Edge.Flow && "incorrect edge flow");
224145449b1SDimitry Andric uint64_t EdgeCapacity = uint64_t(Edge.Capacity - Edge.Flow);
225145449b1SDimitry Andric PathCapacity = std::min(PathCapacity, EdgeCapacity);
226145449b1SDimitry Andric
227145449b1SDimitry Andric Now = Pred;
228145449b1SDimitry Andric }
229145449b1SDimitry Andric return PathCapacity;
230145449b1SDimitry Andric }
231145449b1SDimitry Andric
232f65dcba8SDimitry Andric /// Check for existence of an augmenting path with a positive capacity.
findAugmentingPath()233f65dcba8SDimitry Andric bool findAugmentingPath() {
234f65dcba8SDimitry Andric // Initialize data structures
235f65dcba8SDimitry Andric for (auto &Node : Nodes) {
236f65dcba8SDimitry Andric Node.Distance = INF;
237f65dcba8SDimitry Andric Node.ParentNode = uint64_t(-1);
238f65dcba8SDimitry Andric Node.ParentEdgeIndex = uint64_t(-1);
239f65dcba8SDimitry Andric Node.Taken = false;
240f65dcba8SDimitry Andric }
241f65dcba8SDimitry Andric
242f65dcba8SDimitry Andric std::queue<uint64_t> Queue;
243f65dcba8SDimitry Andric Queue.push(Source);
244f65dcba8SDimitry Andric Nodes[Source].Distance = 0;
245f65dcba8SDimitry Andric Nodes[Source].Taken = true;
246f65dcba8SDimitry Andric while (!Queue.empty()) {
247f65dcba8SDimitry Andric uint64_t Src = Queue.front();
248f65dcba8SDimitry Andric Queue.pop();
249f65dcba8SDimitry Andric Nodes[Src].Taken = false;
250f65dcba8SDimitry Andric // Although the residual network contains edges with negative costs
251f65dcba8SDimitry Andric // (in particular, backward edges), it can be shown that there are no
252f65dcba8SDimitry Andric // negative-weight cycles and the following two invariants are maintained:
253f65dcba8SDimitry Andric // (i) Dist[Source, V] >= 0 and (ii) Dist[V, Target] >= 0 for all nodes V,
254f65dcba8SDimitry Andric // where Dist is the length of the shortest path between two nodes. This
255f65dcba8SDimitry Andric // allows to prune the search-space of the path-finding algorithm using
256f65dcba8SDimitry Andric // the following early-stop criteria:
257f65dcba8SDimitry Andric // -- If we find a path with zero-distance from Source to Target, stop the
258f65dcba8SDimitry Andric // search, as the path is the shortest since Dist[Source, Target] >= 0;
259f65dcba8SDimitry Andric // -- If we have Dist[Source, V] > Dist[Source, Target], then do not
260f65dcba8SDimitry Andric // process node V, as it is guaranteed _not_ to be on a shortest path
261f65dcba8SDimitry Andric // from Source to Target; it follows from inequalities
262f65dcba8SDimitry Andric // Dist[Source, Target] >= Dist[Source, V] + Dist[V, Target]
263f65dcba8SDimitry Andric // >= Dist[Source, V]
264e3b55780SDimitry Andric if (!Params.EvenFlowDistribution && Nodes[Target].Distance == 0)
265f65dcba8SDimitry Andric break;
266f65dcba8SDimitry Andric if (Nodes[Src].Distance > Nodes[Target].Distance)
267f65dcba8SDimitry Andric continue;
268f65dcba8SDimitry Andric
269f65dcba8SDimitry Andric // Process adjacent edges
270f65dcba8SDimitry Andric for (uint64_t EdgeIdx = 0; EdgeIdx < Edges[Src].size(); EdgeIdx++) {
271f65dcba8SDimitry Andric auto &Edge = Edges[Src][EdgeIdx];
272f65dcba8SDimitry Andric if (Edge.Flow < Edge.Capacity) {
273f65dcba8SDimitry Andric uint64_t Dst = Edge.Dst;
274f65dcba8SDimitry Andric int64_t NewDistance = Nodes[Src].Distance + Edge.Cost;
275f65dcba8SDimitry Andric if (Nodes[Dst].Distance > NewDistance) {
276f65dcba8SDimitry Andric // Update the distance and the parent node/edge
277f65dcba8SDimitry Andric Nodes[Dst].Distance = NewDistance;
278f65dcba8SDimitry Andric Nodes[Dst].ParentNode = Src;
279f65dcba8SDimitry Andric Nodes[Dst].ParentEdgeIndex = EdgeIdx;
280f65dcba8SDimitry Andric // Add the node to the queue, if it is not there yet
281f65dcba8SDimitry Andric if (!Nodes[Dst].Taken) {
282f65dcba8SDimitry Andric Queue.push(Dst);
283f65dcba8SDimitry Andric Nodes[Dst].Taken = true;
284f65dcba8SDimitry Andric }
285f65dcba8SDimitry Andric }
286f65dcba8SDimitry Andric }
287f65dcba8SDimitry Andric }
288f65dcba8SDimitry Andric }
289f65dcba8SDimitry Andric
290f65dcba8SDimitry Andric return Nodes[Target].Distance != INF;
291f65dcba8SDimitry Andric }
292f65dcba8SDimitry Andric
293f65dcba8SDimitry Andric /// Update the current flow along the augmenting path.
augmentFlowAlongPath(uint64_t PathCapacity)294145449b1SDimitry Andric void augmentFlowAlongPath(uint64_t PathCapacity) {
29577fc4c14SDimitry Andric assert(PathCapacity > 0 && "found an incorrect augmenting path");
296145449b1SDimitry Andric uint64_t Now = Target;
297f65dcba8SDimitry Andric while (Now != Source) {
298f65dcba8SDimitry Andric uint64_t Pred = Nodes[Now].ParentNode;
299f65dcba8SDimitry Andric auto &Edge = Edges[Pred][Nodes[Now].ParentEdgeIndex];
300f65dcba8SDimitry Andric auto &RevEdge = Edges[Now][Edge.RevEdgeIndex];
301f65dcba8SDimitry Andric
302f65dcba8SDimitry Andric Edge.Flow += PathCapacity;
303f65dcba8SDimitry Andric RevEdge.Flow -= PathCapacity;
304f65dcba8SDimitry Andric
305f65dcba8SDimitry Andric Now = Pred;
306f65dcba8SDimitry Andric }
307f65dcba8SDimitry Andric }
308f65dcba8SDimitry Andric
309145449b1SDimitry Andric /// Find an Augmenting DAG order using a modified version of DFS in which we
310145449b1SDimitry Andric /// can visit a node multiple times. In the DFS search, when scanning each
311145449b1SDimitry Andric /// edge out of a node, continue search at Edge.Dst endpoint if it has not
312145449b1SDimitry Andric /// been discovered yet and its NumCalls < MaxDfsCalls. The algorithm
313145449b1SDimitry Andric /// runs in O(MaxDfsCalls * |Edges| + |Nodes|) time.
314145449b1SDimitry Andric /// It returns an Augmenting Order (Taken nodes in decreasing Finish time)
315145449b1SDimitry Andric /// that starts with Source and ends with Target.
findAugmentingDAG()316145449b1SDimitry Andric std::vector<uint64_t> findAugmentingDAG() {
317145449b1SDimitry Andric // We use a stack based implemenation of DFS to avoid recursion.
318145449b1SDimitry Andric // Defining DFS data structures:
319145449b1SDimitry Andric // A pair (NodeIdx, EdgeIdx) at the top of the Stack denotes that
320145449b1SDimitry Andric // - we are currently visiting Nodes[NodeIdx] and
321145449b1SDimitry Andric // - the next edge to scan is Edges[NodeIdx][EdgeIdx]
322145449b1SDimitry Andric typedef std::pair<uint64_t, uint64_t> StackItemType;
323145449b1SDimitry Andric std::stack<StackItemType> Stack;
324145449b1SDimitry Andric std::vector<uint64_t> AugmentingOrder;
325145449b1SDimitry Andric
326145449b1SDimitry Andric // Phase 0: Initialize Node attributes and Time for DFS run
327145449b1SDimitry Andric for (auto &Node : Nodes) {
328145449b1SDimitry Andric Node.Discovery = 0;
329145449b1SDimitry Andric Node.Finish = 0;
330145449b1SDimitry Andric Node.NumCalls = 0;
331145449b1SDimitry Andric Node.Taken = false;
332145449b1SDimitry Andric }
333145449b1SDimitry Andric uint64_t Time = 0;
334145449b1SDimitry Andric // Mark Target as Taken
335145449b1SDimitry Andric // Taken attribute will be propagated backwards from Target towards Source
336145449b1SDimitry Andric Nodes[Target].Taken = true;
337145449b1SDimitry Andric
338145449b1SDimitry Andric // Phase 1: Start DFS traversal from Source
339145449b1SDimitry Andric Stack.emplace(Source, 0);
340145449b1SDimitry Andric Nodes[Source].Discovery = ++Time;
341145449b1SDimitry Andric while (!Stack.empty()) {
342145449b1SDimitry Andric auto NodeIdx = Stack.top().first;
343145449b1SDimitry Andric auto EdgeIdx = Stack.top().second;
344145449b1SDimitry Andric
345145449b1SDimitry Andric // If we haven't scanned all edges out of NodeIdx, continue scanning
346145449b1SDimitry Andric if (EdgeIdx < Edges[NodeIdx].size()) {
347145449b1SDimitry Andric auto &Edge = Edges[NodeIdx][EdgeIdx];
348145449b1SDimitry Andric auto &Dst = Nodes[Edge.Dst];
349145449b1SDimitry Andric Stack.top().second++;
350145449b1SDimitry Andric
351145449b1SDimitry Andric if (Edge.OnShortestPath) {
352145449b1SDimitry Andric // If we haven't seen Edge.Dst so far, continue DFS search there
353e3b55780SDimitry Andric if (Dst.Discovery == 0 && Dst.NumCalls < MaxDfsCalls) {
354145449b1SDimitry Andric Dst.Discovery = ++Time;
355145449b1SDimitry Andric Stack.emplace(Edge.Dst, 0);
356145449b1SDimitry Andric Dst.NumCalls++;
357145449b1SDimitry Andric } else if (Dst.Taken && Dst.Finish != 0) {
358145449b1SDimitry Andric // Else, if Edge.Dst already have a path to Target, so that NodeIdx
359145449b1SDimitry Andric Nodes[NodeIdx].Taken = true;
360145449b1SDimitry Andric }
361145449b1SDimitry Andric }
362145449b1SDimitry Andric } else {
363145449b1SDimitry Andric // If we are done scanning all edge out of NodeIdx
364145449b1SDimitry Andric Stack.pop();
365145449b1SDimitry Andric // If we haven't found a path from NodeIdx to Target, forget about it
366145449b1SDimitry Andric if (!Nodes[NodeIdx].Taken) {
367145449b1SDimitry Andric Nodes[NodeIdx].Discovery = 0;
368145449b1SDimitry Andric } else {
369145449b1SDimitry Andric // If we have found a path from NodeIdx to Target, then finish NodeIdx
370145449b1SDimitry Andric // and propagate Taken flag to DFS parent unless at the Source
371145449b1SDimitry Andric Nodes[NodeIdx].Finish = ++Time;
372145449b1SDimitry Andric // NodeIdx == Source if and only if the stack is empty
373145449b1SDimitry Andric if (NodeIdx != Source) {
374145449b1SDimitry Andric assert(!Stack.empty() && "empty stack while running dfs");
375145449b1SDimitry Andric Nodes[Stack.top().first].Taken = true;
376145449b1SDimitry Andric }
377145449b1SDimitry Andric AugmentingOrder.push_back(NodeIdx);
378145449b1SDimitry Andric }
379145449b1SDimitry Andric }
380145449b1SDimitry Andric }
381145449b1SDimitry Andric // Nodes are collected decreasing Finish time, so the order is reversed
382145449b1SDimitry Andric std::reverse(AugmentingOrder.begin(), AugmentingOrder.end());
383145449b1SDimitry Andric
384145449b1SDimitry Andric // Phase 2: Extract all forward (DAG) edges and fill in AugmentingEdges
385145449b1SDimitry Andric for (size_t Src : AugmentingOrder) {
386145449b1SDimitry Andric AugmentingEdges[Src].clear();
387145449b1SDimitry Andric for (auto &Edge : Edges[Src]) {
388145449b1SDimitry Andric uint64_t Dst = Edge.Dst;
389145449b1SDimitry Andric if (Edge.OnShortestPath && Nodes[Src].Taken && Nodes[Dst].Taken &&
390145449b1SDimitry Andric Nodes[Dst].Finish < Nodes[Src].Finish) {
391145449b1SDimitry Andric AugmentingEdges[Src].push_back(&Edge);
392145449b1SDimitry Andric }
393145449b1SDimitry Andric }
394145449b1SDimitry Andric assert((Src == Target || !AugmentingEdges[Src].empty()) &&
395145449b1SDimitry Andric "incorrectly constructed augmenting edges");
396145449b1SDimitry Andric }
397145449b1SDimitry Andric
398145449b1SDimitry Andric return AugmentingOrder;
399145449b1SDimitry Andric }
400145449b1SDimitry Andric
401145449b1SDimitry Andric /// Update the current flow along the given (acyclic) subgraph specified by
402145449b1SDimitry Andric /// the vertex order, AugmentingOrder. The objective is to send as much flow
403145449b1SDimitry Andric /// as possible while evenly distributing flow among successors of each node.
404145449b1SDimitry Andric /// After the update at least one edge is saturated.
augmentFlowAlongDAG(const std::vector<uint64_t> & AugmentingOrder)405145449b1SDimitry Andric bool augmentFlowAlongDAG(const std::vector<uint64_t> &AugmentingOrder) {
406145449b1SDimitry Andric // Phase 0: Initialization
407145449b1SDimitry Andric for (uint64_t Src : AugmentingOrder) {
408145449b1SDimitry Andric Nodes[Src].FracFlow = 0;
409145449b1SDimitry Andric Nodes[Src].IntFlow = 0;
410145449b1SDimitry Andric for (auto &Edge : AugmentingEdges[Src]) {
411145449b1SDimitry Andric Edge->AugmentedFlow = 0;
412145449b1SDimitry Andric }
413145449b1SDimitry Andric }
414145449b1SDimitry Andric
415145449b1SDimitry Andric // Phase 1: Send a unit of fractional flow along the DAG
416145449b1SDimitry Andric uint64_t MaxFlowAmount = INF;
417145449b1SDimitry Andric Nodes[Source].FracFlow = 1.0;
418145449b1SDimitry Andric for (uint64_t Src : AugmentingOrder) {
419145449b1SDimitry Andric assert((Src == Target || Nodes[Src].FracFlow > 0.0) &&
420145449b1SDimitry Andric "incorrectly computed fractional flow");
421145449b1SDimitry Andric // Distribute flow evenly among successors of Src
422145449b1SDimitry Andric uint64_t Degree = AugmentingEdges[Src].size();
423145449b1SDimitry Andric for (auto &Edge : AugmentingEdges[Src]) {
424145449b1SDimitry Andric double EdgeFlow = Nodes[Src].FracFlow / Degree;
425145449b1SDimitry Andric Nodes[Edge->Dst].FracFlow += EdgeFlow;
426145449b1SDimitry Andric if (Edge->Capacity == INF)
427145449b1SDimitry Andric continue;
428145449b1SDimitry Andric uint64_t MaxIntFlow = double(Edge->Capacity - Edge->Flow) / EdgeFlow;
429145449b1SDimitry Andric MaxFlowAmount = std::min(MaxFlowAmount, MaxIntFlow);
430145449b1SDimitry Andric }
431145449b1SDimitry Andric }
432145449b1SDimitry Andric // Stop early if we cannot send any (integral) flow from Source to Target
433145449b1SDimitry Andric if (MaxFlowAmount == 0)
434145449b1SDimitry Andric return false;
435145449b1SDimitry Andric
436145449b1SDimitry Andric // Phase 2: Send an integral flow of MaxFlowAmount
437145449b1SDimitry Andric Nodes[Source].IntFlow = MaxFlowAmount;
438145449b1SDimitry Andric for (uint64_t Src : AugmentingOrder) {
439145449b1SDimitry Andric if (Src == Target)
440145449b1SDimitry Andric break;
441145449b1SDimitry Andric // Distribute flow evenly among successors of Src, rounding up to make
442145449b1SDimitry Andric // sure all flow is sent
443145449b1SDimitry Andric uint64_t Degree = AugmentingEdges[Src].size();
444145449b1SDimitry Andric // We are guaranteeed that Node[Src].IntFlow <= SuccFlow * Degree
445145449b1SDimitry Andric uint64_t SuccFlow = (Nodes[Src].IntFlow + Degree - 1) / Degree;
446145449b1SDimitry Andric for (auto &Edge : AugmentingEdges[Src]) {
447145449b1SDimitry Andric uint64_t Dst = Edge->Dst;
448145449b1SDimitry Andric uint64_t EdgeFlow = std::min(Nodes[Src].IntFlow, SuccFlow);
449145449b1SDimitry Andric EdgeFlow = std::min(EdgeFlow, uint64_t(Edge->Capacity - Edge->Flow));
450145449b1SDimitry Andric Nodes[Dst].IntFlow += EdgeFlow;
451145449b1SDimitry Andric Nodes[Src].IntFlow -= EdgeFlow;
452145449b1SDimitry Andric Edge->AugmentedFlow += EdgeFlow;
453145449b1SDimitry Andric }
454145449b1SDimitry Andric }
455145449b1SDimitry Andric assert(Nodes[Target].IntFlow <= MaxFlowAmount);
456145449b1SDimitry Andric Nodes[Target].IntFlow = 0;
457145449b1SDimitry Andric
458145449b1SDimitry Andric // Phase 3: Send excess flow back traversing the nodes backwards.
459145449b1SDimitry Andric // Because of rounding, not all flow can be sent along the edges of Src.
460145449b1SDimitry Andric // Hence, sending the remaining flow back to maintain flow conservation
461145449b1SDimitry Andric for (size_t Idx = AugmentingOrder.size() - 1; Idx > 0; Idx--) {
462145449b1SDimitry Andric uint64_t Src = AugmentingOrder[Idx - 1];
463145449b1SDimitry Andric // Try to send excess flow back along each edge.
464145449b1SDimitry Andric // Make sure we only send back flow we just augmented (AugmentedFlow).
465145449b1SDimitry Andric for (auto &Edge : AugmentingEdges[Src]) {
466145449b1SDimitry Andric uint64_t Dst = Edge->Dst;
467145449b1SDimitry Andric if (Nodes[Dst].IntFlow == 0)
468145449b1SDimitry Andric continue;
469145449b1SDimitry Andric uint64_t EdgeFlow = std::min(Nodes[Dst].IntFlow, Edge->AugmentedFlow);
470145449b1SDimitry Andric Nodes[Dst].IntFlow -= EdgeFlow;
471145449b1SDimitry Andric Nodes[Src].IntFlow += EdgeFlow;
472145449b1SDimitry Andric Edge->AugmentedFlow -= EdgeFlow;
473145449b1SDimitry Andric }
474145449b1SDimitry Andric }
475145449b1SDimitry Andric
476145449b1SDimitry Andric // Phase 4: Update flow values along all edges
477145449b1SDimitry Andric bool HasSaturatedEdges = false;
478145449b1SDimitry Andric for (uint64_t Src : AugmentingOrder) {
479145449b1SDimitry Andric // Verify that we have sent all the excess flow from the node
480145449b1SDimitry Andric assert(Src == Source || Nodes[Src].IntFlow == 0);
481145449b1SDimitry Andric for (auto &Edge : AugmentingEdges[Src]) {
482145449b1SDimitry Andric assert(uint64_t(Edge->Capacity - Edge->Flow) >= Edge->AugmentedFlow);
483145449b1SDimitry Andric // Update flow values along the edge and its reverse copy
484145449b1SDimitry Andric auto &RevEdge = Edges[Edge->Dst][Edge->RevEdgeIndex];
485145449b1SDimitry Andric Edge->Flow += Edge->AugmentedFlow;
486145449b1SDimitry Andric RevEdge.Flow -= Edge->AugmentedFlow;
487145449b1SDimitry Andric if (Edge->Capacity == Edge->Flow && Edge->AugmentedFlow > 0)
488145449b1SDimitry Andric HasSaturatedEdges = true;
489145449b1SDimitry Andric }
490145449b1SDimitry Andric }
491145449b1SDimitry Andric
492145449b1SDimitry Andric // The augmentation is successful iff at least one edge becomes saturated
493145449b1SDimitry Andric return HasSaturatedEdges;
494145449b1SDimitry Andric }
495145449b1SDimitry Andric
496145449b1SDimitry Andric /// Identify candidate (shortest) edges for augmentation.
identifyShortestEdges(uint64_t PathCapacity)497145449b1SDimitry Andric void identifyShortestEdges(uint64_t PathCapacity) {
498145449b1SDimitry Andric assert(PathCapacity > 0 && "found an incorrect augmenting DAG");
499145449b1SDimitry Andric // To make sure the augmentation DAG contains only edges with large residual
500145449b1SDimitry Andric // capacity, we prune all edges whose capacity is below a fraction of
501145449b1SDimitry Andric // the capacity of the augmented path.
502145449b1SDimitry Andric // (All edges of the path itself are always in the DAG)
503145449b1SDimitry Andric uint64_t MinCapacity = std::max(PathCapacity / 2, uint64_t(1));
504145449b1SDimitry Andric
505145449b1SDimitry Andric // Decide which edges are on a shortest path from Source to Target
506145449b1SDimitry Andric for (size_t Src = 0; Src < Nodes.size(); Src++) {
507145449b1SDimitry Andric // An edge cannot be augmenting if the endpoint has large distance
508145449b1SDimitry Andric if (Nodes[Src].Distance > Nodes[Target].Distance)
509145449b1SDimitry Andric continue;
510145449b1SDimitry Andric
511145449b1SDimitry Andric for (auto &Edge : Edges[Src]) {
512145449b1SDimitry Andric uint64_t Dst = Edge.Dst;
513145449b1SDimitry Andric Edge.OnShortestPath =
514145449b1SDimitry Andric Src != Target && Dst != Source &&
515145449b1SDimitry Andric Nodes[Dst].Distance <= Nodes[Target].Distance &&
516145449b1SDimitry Andric Nodes[Dst].Distance == Nodes[Src].Distance + Edge.Cost &&
517145449b1SDimitry Andric Edge.Capacity > Edge.Flow &&
518145449b1SDimitry Andric uint64_t(Edge.Capacity - Edge.Flow) >= MinCapacity;
519145449b1SDimitry Andric }
520145449b1SDimitry Andric }
521145449b1SDimitry Andric }
522145449b1SDimitry Andric
523e3b55780SDimitry Andric /// Maximum number of DFS iterations for DAG finding.
524e3b55780SDimitry Andric static constexpr uint64_t MaxDfsCalls = 10;
525e3b55780SDimitry Andric
5266f8fc217SDimitry Andric /// A node in a flow network.
527f65dcba8SDimitry Andric struct Node {
528f65dcba8SDimitry Andric /// The cost of the cheapest path from the source to the current node.
529f65dcba8SDimitry Andric int64_t Distance;
530f65dcba8SDimitry Andric /// The node preceding the current one in the path.
531f65dcba8SDimitry Andric uint64_t ParentNode;
532f65dcba8SDimitry Andric /// The index of the edge between ParentNode and the current node.
533f65dcba8SDimitry Andric uint64_t ParentEdgeIndex;
534f65dcba8SDimitry Andric /// An indicator of whether the current node is in a queue.
535f65dcba8SDimitry Andric bool Taken;
536145449b1SDimitry Andric
537145449b1SDimitry Andric /// Data fields utilized in DAG-augmentation:
538145449b1SDimitry Andric /// Fractional flow.
539145449b1SDimitry Andric double FracFlow;
540145449b1SDimitry Andric /// Integral flow.
541145449b1SDimitry Andric uint64_t IntFlow;
542145449b1SDimitry Andric /// Discovery time.
543145449b1SDimitry Andric uint64_t Discovery;
544145449b1SDimitry Andric /// Finish time.
545145449b1SDimitry Andric uint64_t Finish;
546145449b1SDimitry Andric /// NumCalls.
547145449b1SDimitry Andric uint64_t NumCalls;
548f65dcba8SDimitry Andric };
549145449b1SDimitry Andric
550f65dcba8SDimitry Andric /// An edge in a flow network.
551f65dcba8SDimitry Andric struct Edge {
552f65dcba8SDimitry Andric /// The cost of the edge.
553f65dcba8SDimitry Andric int64_t Cost;
554f65dcba8SDimitry Andric /// The capacity of the edge.
555f65dcba8SDimitry Andric int64_t Capacity;
556f65dcba8SDimitry Andric /// The current flow on the edge.
557f65dcba8SDimitry Andric int64_t Flow;
558f65dcba8SDimitry Andric /// The destination node of the edge.
559f65dcba8SDimitry Andric uint64_t Dst;
560f65dcba8SDimitry Andric /// The index of the reverse edge between Dst and the current node.
561f65dcba8SDimitry Andric uint64_t RevEdgeIndex;
562145449b1SDimitry Andric
563145449b1SDimitry Andric /// Data fields utilized in DAG-augmentation:
564145449b1SDimitry Andric /// Whether the edge is currently on a shortest path from Source to Target.
565145449b1SDimitry Andric bool OnShortestPath;
566145449b1SDimitry Andric /// Extra flow along the edge.
567145449b1SDimitry Andric uint64_t AugmentedFlow;
568f65dcba8SDimitry Andric };
569f65dcba8SDimitry Andric
570f65dcba8SDimitry Andric /// The set of network nodes.
571f65dcba8SDimitry Andric std::vector<Node> Nodes;
572f65dcba8SDimitry Andric /// The set of network edges.
573f65dcba8SDimitry Andric std::vector<std::vector<Edge>> Edges;
574f65dcba8SDimitry Andric /// Source node of the flow.
575f65dcba8SDimitry Andric uint64_t Source;
576f65dcba8SDimitry Andric /// Target (sink) node of the flow.
577f65dcba8SDimitry Andric uint64_t Target;
578145449b1SDimitry Andric /// Augmenting edges.
579145449b1SDimitry Andric std::vector<std::vector<Edge *>> AugmentingEdges;
580e3b55780SDimitry Andric /// Params for flow computation.
581e3b55780SDimitry Andric const ProfiParams &Params;
582f65dcba8SDimitry Andric };
583f65dcba8SDimitry Andric
584e3b55780SDimitry Andric /// A post-processing adjustment of the control flow. It applies two steps by
58577fc4c14SDimitry Andric /// rerouting some flow and making it more realistic:
58677fc4c14SDimitry Andric ///
58777fc4c14SDimitry Andric /// - First, it removes all isolated components ("islands") with a positive flow
58877fc4c14SDimitry Andric /// that are unreachable from the entry block. For every such component, we
58977fc4c14SDimitry Andric /// find the shortest from the entry to an exit passing through the component,
59077fc4c14SDimitry Andric /// and increase the flow by one unit along the path.
59177fc4c14SDimitry Andric ///
59277fc4c14SDimitry Andric /// - Second, it identifies all "unknown subgraphs" consisting of basic blocks
59377fc4c14SDimitry Andric /// with no sampled counts. Then it rebalnces the flow that goes through such
59477fc4c14SDimitry Andric /// a subgraph so that each branch is taken with probability 50%.
59577fc4c14SDimitry Andric /// An unknown subgraph is such that for every two nodes u and v:
59677fc4c14SDimitry Andric /// - u dominates v and u is not unknown;
59777fc4c14SDimitry Andric /// - v post-dominates u; and
59877fc4c14SDimitry Andric /// - all inner-nodes of all (u,v)-paths are unknown.
59977fc4c14SDimitry Andric ///
60077fc4c14SDimitry Andric class FlowAdjuster {
60177fc4c14SDimitry Andric public:
FlowAdjuster(const ProfiParams & Params,FlowFunction & Func)602e3b55780SDimitry Andric FlowAdjuster(const ProfiParams &Params, FlowFunction &Func)
603e3b55780SDimitry Andric : Params(Params), Func(Func) {}
604e3b55780SDimitry Andric
605e3b55780SDimitry Andric /// Apply the post-processing.
run()606e3b55780SDimitry Andric void run() {
607e3b55780SDimitry Andric if (Params.JoinIslands) {
608e3b55780SDimitry Andric // Adjust the flow to get rid of isolated components
609e3b55780SDimitry Andric joinIsolatedComponents();
61077fc4c14SDimitry Andric }
61177fc4c14SDimitry Andric
612e3b55780SDimitry Andric if (Params.RebalanceUnknown) {
613e3b55780SDimitry Andric // Rebalance the flow inside unknown subgraphs
61477fc4c14SDimitry Andric rebalanceUnknownSubgraphs();
61577fc4c14SDimitry Andric }
616e3b55780SDimitry Andric }
61777fc4c14SDimitry Andric
61877fc4c14SDimitry Andric private:
joinIsolatedComponents()61977fc4c14SDimitry Andric void joinIsolatedComponents() {
62077fc4c14SDimitry Andric // Find blocks that are reachable from the source
6216f8fc217SDimitry Andric auto Visited = BitVector(NumBlocks(), false);
62277fc4c14SDimitry Andric findReachable(Func.Entry, Visited);
62377fc4c14SDimitry Andric
62477fc4c14SDimitry Andric // Iterate over all non-reachable blocks and adjust their weights
62577fc4c14SDimitry Andric for (uint64_t I = 0; I < NumBlocks(); I++) {
62677fc4c14SDimitry Andric auto &Block = Func.Blocks[I];
62777fc4c14SDimitry Andric if (Block.Flow > 0 && !Visited[I]) {
62877fc4c14SDimitry Andric // Find a path from the entry to an exit passing through the block I
62977fc4c14SDimitry Andric auto Path = findShortestPath(I);
63077fc4c14SDimitry Andric // Increase the flow along the path
63177fc4c14SDimitry Andric assert(Path.size() > 0 && Path[0]->Source == Func.Entry &&
63277fc4c14SDimitry Andric "incorrectly computed path adjusting control flow");
63377fc4c14SDimitry Andric Func.Blocks[Func.Entry].Flow += 1;
63477fc4c14SDimitry Andric for (auto &Jump : Path) {
63577fc4c14SDimitry Andric Jump->Flow += 1;
63677fc4c14SDimitry Andric Func.Blocks[Jump->Target].Flow += 1;
63777fc4c14SDimitry Andric // Update reachability
63877fc4c14SDimitry Andric findReachable(Jump->Target, Visited);
63977fc4c14SDimitry Andric }
64077fc4c14SDimitry Andric }
64177fc4c14SDimitry Andric }
64277fc4c14SDimitry Andric }
64377fc4c14SDimitry Andric
64477fc4c14SDimitry Andric /// Run BFS from a given block along the jumps with a positive flow and mark
64577fc4c14SDimitry Andric /// all reachable blocks.
findReachable(uint64_t Src,BitVector & Visited)6466f8fc217SDimitry Andric void findReachable(uint64_t Src, BitVector &Visited) {
64777fc4c14SDimitry Andric if (Visited[Src])
64877fc4c14SDimitry Andric return;
64977fc4c14SDimitry Andric std::queue<uint64_t> Queue;
65077fc4c14SDimitry Andric Queue.push(Src);
65177fc4c14SDimitry Andric Visited[Src] = true;
65277fc4c14SDimitry Andric while (!Queue.empty()) {
65377fc4c14SDimitry Andric Src = Queue.front();
65477fc4c14SDimitry Andric Queue.pop();
655e3b55780SDimitry Andric for (auto *Jump : Func.Blocks[Src].SuccJumps) {
65677fc4c14SDimitry Andric uint64_t Dst = Jump->Target;
65777fc4c14SDimitry Andric if (Jump->Flow > 0 && !Visited[Dst]) {
65877fc4c14SDimitry Andric Queue.push(Dst);
65977fc4c14SDimitry Andric Visited[Dst] = true;
66077fc4c14SDimitry Andric }
66177fc4c14SDimitry Andric }
66277fc4c14SDimitry Andric }
66377fc4c14SDimitry Andric }
66477fc4c14SDimitry Andric
66577fc4c14SDimitry Andric /// Find the shortest path from the entry block to an exit block passing
66677fc4c14SDimitry Andric /// through a given block.
findShortestPath(uint64_t BlockIdx)66777fc4c14SDimitry Andric std::vector<FlowJump *> findShortestPath(uint64_t BlockIdx) {
66877fc4c14SDimitry Andric // A path from the entry block to BlockIdx
66977fc4c14SDimitry Andric auto ForwardPath = findShortestPath(Func.Entry, BlockIdx);
67077fc4c14SDimitry Andric // A path from BlockIdx to an exit block
67177fc4c14SDimitry Andric auto BackwardPath = findShortestPath(BlockIdx, AnyExitBlock);
67277fc4c14SDimitry Andric
67377fc4c14SDimitry Andric // Concatenate the two paths
67477fc4c14SDimitry Andric std::vector<FlowJump *> Result;
67577fc4c14SDimitry Andric Result.insert(Result.end(), ForwardPath.begin(), ForwardPath.end());
67677fc4c14SDimitry Andric Result.insert(Result.end(), BackwardPath.begin(), BackwardPath.end());
67777fc4c14SDimitry Andric return Result;
67877fc4c14SDimitry Andric }
67977fc4c14SDimitry Andric
68077fc4c14SDimitry Andric /// Apply the Dijkstra algorithm to find the shortest path from a given
68177fc4c14SDimitry Andric /// Source to a given Target block.
68277fc4c14SDimitry Andric /// If Target == -1, then the path ends at an exit block.
findShortestPath(uint64_t Source,uint64_t Target)68377fc4c14SDimitry Andric std::vector<FlowJump *> findShortestPath(uint64_t Source, uint64_t Target) {
68477fc4c14SDimitry Andric // Quit early, if possible
68577fc4c14SDimitry Andric if (Source == Target)
68677fc4c14SDimitry Andric return std::vector<FlowJump *>();
68777fc4c14SDimitry Andric if (Func.Blocks[Source].isExit() && Target == AnyExitBlock)
68877fc4c14SDimitry Andric return std::vector<FlowJump *>();
68977fc4c14SDimitry Andric
69077fc4c14SDimitry Andric // Initialize data structures
69177fc4c14SDimitry Andric auto Distance = std::vector<int64_t>(NumBlocks(), INF);
69277fc4c14SDimitry Andric auto Parent = std::vector<FlowJump *>(NumBlocks(), nullptr);
69377fc4c14SDimitry Andric Distance[Source] = 0;
69477fc4c14SDimitry Andric std::set<std::pair<uint64_t, uint64_t>> Queue;
69577fc4c14SDimitry Andric Queue.insert(std::make_pair(Distance[Source], Source));
69677fc4c14SDimitry Andric
69777fc4c14SDimitry Andric // Run the Dijkstra algorithm
69877fc4c14SDimitry Andric while (!Queue.empty()) {
69977fc4c14SDimitry Andric uint64_t Src = Queue.begin()->second;
70077fc4c14SDimitry Andric Queue.erase(Queue.begin());
70177fc4c14SDimitry Andric // If we found a solution, quit early
70277fc4c14SDimitry Andric if (Src == Target ||
70377fc4c14SDimitry Andric (Func.Blocks[Src].isExit() && Target == AnyExitBlock))
70477fc4c14SDimitry Andric break;
70577fc4c14SDimitry Andric
706e3b55780SDimitry Andric for (auto *Jump : Func.Blocks[Src].SuccJumps) {
70777fc4c14SDimitry Andric uint64_t Dst = Jump->Target;
70877fc4c14SDimitry Andric int64_t JumpDist = jumpDistance(Jump);
70977fc4c14SDimitry Andric if (Distance[Dst] > Distance[Src] + JumpDist) {
71077fc4c14SDimitry Andric Queue.erase(std::make_pair(Distance[Dst], Dst));
71177fc4c14SDimitry Andric
71277fc4c14SDimitry Andric Distance[Dst] = Distance[Src] + JumpDist;
71377fc4c14SDimitry Andric Parent[Dst] = Jump;
71477fc4c14SDimitry Andric
71577fc4c14SDimitry Andric Queue.insert(std::make_pair(Distance[Dst], Dst));
71677fc4c14SDimitry Andric }
71777fc4c14SDimitry Andric }
71877fc4c14SDimitry Andric }
71977fc4c14SDimitry Andric // If Target is not provided, find the closest exit block
72077fc4c14SDimitry Andric if (Target == AnyExitBlock) {
72177fc4c14SDimitry Andric for (uint64_t I = 0; I < NumBlocks(); I++) {
72277fc4c14SDimitry Andric if (Func.Blocks[I].isExit() && Parent[I] != nullptr) {
72377fc4c14SDimitry Andric if (Target == AnyExitBlock || Distance[Target] > Distance[I]) {
72477fc4c14SDimitry Andric Target = I;
72577fc4c14SDimitry Andric }
72677fc4c14SDimitry Andric }
72777fc4c14SDimitry Andric }
72877fc4c14SDimitry Andric }
72977fc4c14SDimitry Andric assert(Parent[Target] != nullptr && "a path does not exist");
73077fc4c14SDimitry Andric
73177fc4c14SDimitry Andric // Extract the constructed path
73277fc4c14SDimitry Andric std::vector<FlowJump *> Result;
73377fc4c14SDimitry Andric uint64_t Now = Target;
73477fc4c14SDimitry Andric while (Now != Source) {
73577fc4c14SDimitry Andric assert(Now == Parent[Now]->Target && "incorrect parent jump");
73677fc4c14SDimitry Andric Result.push_back(Parent[Now]);
73777fc4c14SDimitry Andric Now = Parent[Now]->Source;
73877fc4c14SDimitry Andric }
73977fc4c14SDimitry Andric // Reverse the path, since it is extracted from Target to Source
74077fc4c14SDimitry Andric std::reverse(Result.begin(), Result.end());
74177fc4c14SDimitry Andric return Result;
74277fc4c14SDimitry Andric }
74377fc4c14SDimitry Andric
74477fc4c14SDimitry Andric /// A distance of a path for a given jump.
74577fc4c14SDimitry Andric /// In order to incite the path to use blocks/jumps with large positive flow,
74677fc4c14SDimitry Andric /// and avoid changing branch probability of outgoing edges drastically,
747145449b1SDimitry Andric /// set the jump distance so as:
748145449b1SDimitry Andric /// - to minimize the number of unlikely jumps used and subject to that,
749145449b1SDimitry Andric /// - to minimize the number of Flow == 0 jumps used and subject to that,
750145449b1SDimitry Andric /// - minimizes total multiplicative Flow increase for the remaining edges.
751145449b1SDimitry Andric /// To capture this objective with integer distances, we round off fractional
752145449b1SDimitry Andric /// parts to a multiple of 1 / BaseDistance.
jumpDistance(FlowJump * Jump) const75377fc4c14SDimitry Andric int64_t jumpDistance(FlowJump *Jump) const {
75477fc4c14SDimitry Andric if (Jump->IsUnlikely)
755e3b55780SDimitry Andric return Params.CostUnlikely;
756e3b55780SDimitry Andric uint64_t BaseDistance =
757e3b55780SDimitry Andric std::max(FlowAdjuster::MinBaseDistance,
758e3b55780SDimitry Andric std::min(Func.Blocks[Func.Entry].Flow,
759e3b55780SDimitry Andric Params.CostUnlikely / (2 * (NumBlocks() + 1))));
76077fc4c14SDimitry Andric if (Jump->Flow > 0)
761145449b1SDimitry Andric return BaseDistance + BaseDistance / Jump->Flow;
762e3b55780SDimitry Andric return 2 * BaseDistance * (NumBlocks() + 1);
76377fc4c14SDimitry Andric };
76477fc4c14SDimitry Andric
NumBlocks() const76577fc4c14SDimitry Andric uint64_t NumBlocks() const { return Func.Blocks.size(); }
76677fc4c14SDimitry Andric
7676f8fc217SDimitry Andric /// Rebalance unknown subgraphs so that the flow is split evenly across the
7686f8fc217SDimitry Andric /// outgoing branches of every block of the subgraph. The method iterates over
7696f8fc217SDimitry Andric /// blocks with known weight and identifies unknown subgraphs rooted at the
7706f8fc217SDimitry Andric /// blocks. Then it verifies if flow rebalancing is feasible and applies it.
rebalanceUnknownSubgraphs()77177fc4c14SDimitry Andric void rebalanceUnknownSubgraphs() {
7726f8fc217SDimitry Andric // Try to find unknown subgraphs from each block
773e3b55780SDimitry Andric for (const FlowBlock &SrcBlock : Func.Blocks) {
7746f8fc217SDimitry Andric // Verify if rebalancing rooted at SrcBlock is feasible
775e3b55780SDimitry Andric if (!canRebalanceAtRoot(&SrcBlock))
77677fc4c14SDimitry Andric continue;
77777fc4c14SDimitry Andric
7786f8fc217SDimitry Andric // Find an unknown subgraphs starting at SrcBlock. Along the way,
7796f8fc217SDimitry Andric // fill in known destinations and intermediate unknown blocks.
7806f8fc217SDimitry Andric std::vector<FlowBlock *> UnknownBlocks;
7816f8fc217SDimitry Andric std::vector<FlowBlock *> KnownDstBlocks;
782e3b55780SDimitry Andric findUnknownSubgraph(&SrcBlock, KnownDstBlocks, UnknownBlocks);
7836f8fc217SDimitry Andric
7846f8fc217SDimitry Andric // Verify if rebalancing of the subgraph is feasible. If the search is
7856f8fc217SDimitry Andric // successful, find the unique destination block (which can be null)
78677fc4c14SDimitry Andric FlowBlock *DstBlock = nullptr;
787e3b55780SDimitry Andric if (!canRebalanceSubgraph(&SrcBlock, KnownDstBlocks, UnknownBlocks,
7886f8fc217SDimitry Andric DstBlock))
78977fc4c14SDimitry Andric continue;
7906f8fc217SDimitry Andric
7916f8fc217SDimitry Andric // We cannot rebalance subgraphs containing cycles among unknown blocks
792e3b55780SDimitry Andric if (!isAcyclicSubgraph(&SrcBlock, DstBlock, UnknownBlocks))
79377fc4c14SDimitry Andric continue;
79477fc4c14SDimitry Andric
79577fc4c14SDimitry Andric // Rebalance the flow
796e3b55780SDimitry Andric rebalanceUnknownSubgraph(&SrcBlock, DstBlock, UnknownBlocks);
79777fc4c14SDimitry Andric }
79877fc4c14SDimitry Andric }
79977fc4c14SDimitry Andric
8006f8fc217SDimitry Andric /// Verify if rebalancing rooted at a given block is possible.
canRebalanceAtRoot(const FlowBlock * SrcBlock)8016f8fc217SDimitry Andric bool canRebalanceAtRoot(const FlowBlock *SrcBlock) {
8026f8fc217SDimitry Andric // Do not attempt to find unknown subgraphs from an unknown or a
8036f8fc217SDimitry Andric // zero-flow block
804e3b55780SDimitry Andric if (SrcBlock->HasUnknownWeight || SrcBlock->Flow == 0)
8056f8fc217SDimitry Andric return false;
8066f8fc217SDimitry Andric
8076f8fc217SDimitry Andric // Do not attempt to process subgraphs from a block w/o unknown sucessors
8086f8fc217SDimitry Andric bool HasUnknownSuccs = false;
809e3b55780SDimitry Andric for (auto *Jump : SrcBlock->SuccJumps) {
810e3b55780SDimitry Andric if (Func.Blocks[Jump->Target].HasUnknownWeight) {
8116f8fc217SDimitry Andric HasUnknownSuccs = true;
8126f8fc217SDimitry Andric break;
8136f8fc217SDimitry Andric }
8146f8fc217SDimitry Andric }
8156f8fc217SDimitry Andric if (!HasUnknownSuccs)
8166f8fc217SDimitry Andric return false;
8176f8fc217SDimitry Andric
8186f8fc217SDimitry Andric return true;
8196f8fc217SDimitry Andric }
8206f8fc217SDimitry Andric
8216f8fc217SDimitry Andric /// Find an unknown subgraph starting at block SrcBlock. The method sets
8226f8fc217SDimitry Andric /// identified destinations, KnownDstBlocks, and intermediate UnknownBlocks.
findUnknownSubgraph(const FlowBlock * SrcBlock,std::vector<FlowBlock * > & KnownDstBlocks,std::vector<FlowBlock * > & UnknownBlocks)8236f8fc217SDimitry Andric void findUnknownSubgraph(const FlowBlock *SrcBlock,
8246f8fc217SDimitry Andric std::vector<FlowBlock *> &KnownDstBlocks,
8256f8fc217SDimitry Andric std::vector<FlowBlock *> &UnknownBlocks) {
82677fc4c14SDimitry Andric // Run BFS from SrcBlock and make sure all paths are going through unknown
827145449b1SDimitry Andric // blocks and end at a known DstBlock
8286f8fc217SDimitry Andric auto Visited = BitVector(NumBlocks(), false);
82977fc4c14SDimitry Andric std::queue<uint64_t> Queue;
83077fc4c14SDimitry Andric
83177fc4c14SDimitry Andric Queue.push(SrcBlock->Index);
83277fc4c14SDimitry Andric Visited[SrcBlock->Index] = true;
83377fc4c14SDimitry Andric while (!Queue.empty()) {
83477fc4c14SDimitry Andric auto &Block = Func.Blocks[Queue.front()];
83577fc4c14SDimitry Andric Queue.pop();
83677fc4c14SDimitry Andric // Process blocks reachable from Block
837e3b55780SDimitry Andric for (auto *Jump : Block.SuccJumps) {
8386f8fc217SDimitry Andric // If Jump can be ignored, skip it
8396f8fc217SDimitry Andric if (ignoreJump(SrcBlock, nullptr, Jump))
8406f8fc217SDimitry Andric continue;
8416f8fc217SDimitry Andric
84277fc4c14SDimitry Andric uint64_t Dst = Jump->Target;
8436f8fc217SDimitry Andric // If Dst has been visited, skip Jump
84477fc4c14SDimitry Andric if (Visited[Dst])
84577fc4c14SDimitry Andric continue;
8466f8fc217SDimitry Andric // Process block Dst
84777fc4c14SDimitry Andric Visited[Dst] = true;
848e3b55780SDimitry Andric if (!Func.Blocks[Dst].HasUnknownWeight) {
8496f8fc217SDimitry Andric KnownDstBlocks.push_back(&Func.Blocks[Dst]);
85077fc4c14SDimitry Andric } else {
85177fc4c14SDimitry Andric Queue.push(Dst);
8526f8fc217SDimitry Andric UnknownBlocks.push_back(&Func.Blocks[Dst]);
8536f8fc217SDimitry Andric }
85477fc4c14SDimitry Andric }
85577fc4c14SDimitry Andric }
85677fc4c14SDimitry Andric }
85777fc4c14SDimitry Andric
8586f8fc217SDimitry Andric /// Verify if rebalancing of the subgraph is feasible. If the checks are
8596f8fc217SDimitry Andric /// successful, set the unique destination block, DstBlock (can be null).
canRebalanceSubgraph(const FlowBlock * SrcBlock,const std::vector<FlowBlock * > & KnownDstBlocks,const std::vector<FlowBlock * > & UnknownBlocks,FlowBlock * & DstBlock)8606f8fc217SDimitry Andric bool canRebalanceSubgraph(const FlowBlock *SrcBlock,
8616f8fc217SDimitry Andric const std::vector<FlowBlock *> &KnownDstBlocks,
8626f8fc217SDimitry Andric const std::vector<FlowBlock *> &UnknownBlocks,
8636f8fc217SDimitry Andric FlowBlock *&DstBlock) {
86477fc4c14SDimitry Andric // If the list of unknown blocks is empty, we don't need rebalancing
8656f8fc217SDimitry Andric if (UnknownBlocks.empty())
86677fc4c14SDimitry Andric return false;
8676f8fc217SDimitry Andric
8686f8fc217SDimitry Andric // If there are multiple known sinks, we can't rebalance
8696f8fc217SDimitry Andric if (KnownDstBlocks.size() > 1)
87077fc4c14SDimitry Andric return false;
8716f8fc217SDimitry Andric DstBlock = KnownDstBlocks.empty() ? nullptr : KnownDstBlocks.front();
8726f8fc217SDimitry Andric
8736f8fc217SDimitry Andric // Verify sinks of the subgraph
874e3b55780SDimitry Andric for (auto *Block : UnknownBlocks) {
8756f8fc217SDimitry Andric if (Block->SuccJumps.empty()) {
8766f8fc217SDimitry Andric // If there are multiple (known and unknown) sinks, we can't rebalance
8776f8fc217SDimitry Andric if (DstBlock != nullptr)
8786f8fc217SDimitry Andric return false;
8796f8fc217SDimitry Andric continue;
8806f8fc217SDimitry Andric }
8816f8fc217SDimitry Andric size_t NumIgnoredJumps = 0;
882e3b55780SDimitry Andric for (auto *Jump : Block->SuccJumps) {
8836f8fc217SDimitry Andric if (ignoreJump(SrcBlock, DstBlock, Jump))
8846f8fc217SDimitry Andric NumIgnoredJumps++;
8856f8fc217SDimitry Andric }
8866f8fc217SDimitry Andric // If there is a non-sink block in UnknownBlocks with all jumps ignored,
8876f8fc217SDimitry Andric // then we can't rebalance
8886f8fc217SDimitry Andric if (NumIgnoredJumps == Block->SuccJumps.size())
88977fc4c14SDimitry Andric return false;
89077fc4c14SDimitry Andric }
89177fc4c14SDimitry Andric
89277fc4c14SDimitry Andric return true;
89377fc4c14SDimitry Andric }
89477fc4c14SDimitry Andric
8956f8fc217SDimitry Andric /// Decide whether the Jump is ignored while processing an unknown subgraphs
8966f8fc217SDimitry Andric /// rooted at basic block SrcBlock with the destination block, DstBlock.
ignoreJump(const FlowBlock * SrcBlock,const FlowBlock * DstBlock,const FlowJump * Jump)8976f8fc217SDimitry Andric bool ignoreJump(const FlowBlock *SrcBlock, const FlowBlock *DstBlock,
8986f8fc217SDimitry Andric const FlowJump *Jump) {
8996f8fc217SDimitry Andric // Ignore unlikely jumps with zero flow
9006f8fc217SDimitry Andric if (Jump->IsUnlikely && Jump->Flow == 0)
9016f8fc217SDimitry Andric return true;
9026f8fc217SDimitry Andric
9036f8fc217SDimitry Andric auto JumpSource = &Func.Blocks[Jump->Source];
9046f8fc217SDimitry Andric auto JumpTarget = &Func.Blocks[Jump->Target];
9056f8fc217SDimitry Andric
9066f8fc217SDimitry Andric // Do not ignore jumps coming into DstBlock
9076f8fc217SDimitry Andric if (DstBlock != nullptr && JumpTarget == DstBlock)
9086f8fc217SDimitry Andric return false;
9096f8fc217SDimitry Andric
9106f8fc217SDimitry Andric // Ignore jumps out of SrcBlock to known blocks
911e3b55780SDimitry Andric if (!JumpTarget->HasUnknownWeight && JumpSource == SrcBlock)
9126f8fc217SDimitry Andric return true;
9136f8fc217SDimitry Andric
9146f8fc217SDimitry Andric // Ignore jumps to known blocks with zero flow
915e3b55780SDimitry Andric if (!JumpTarget->HasUnknownWeight && JumpTarget->Flow == 0)
9166f8fc217SDimitry Andric return true;
9176f8fc217SDimitry Andric
9186f8fc217SDimitry Andric return false;
9196f8fc217SDimitry Andric }
9206f8fc217SDimitry Andric
92177fc4c14SDimitry Andric /// Verify if the given unknown subgraph is acyclic, and if yes, reorder
9226f8fc217SDimitry Andric /// UnknownBlocks in the topological order (so that all jumps are "forward").
isAcyclicSubgraph(const FlowBlock * SrcBlock,const FlowBlock * DstBlock,std::vector<FlowBlock * > & UnknownBlocks)9236f8fc217SDimitry Andric bool isAcyclicSubgraph(const FlowBlock *SrcBlock, const FlowBlock *DstBlock,
9246f8fc217SDimitry Andric std::vector<FlowBlock *> &UnknownBlocks) {
92577fc4c14SDimitry Andric // Extract local in-degrees in the considered subgraph
92677fc4c14SDimitry Andric auto LocalInDegree = std::vector<uint64_t>(NumBlocks(), 0);
9276f8fc217SDimitry Andric auto fillInDegree = [&](const FlowBlock *Block) {
928e3b55780SDimitry Andric for (auto *Jump : Block->SuccJumps) {
9296f8fc217SDimitry Andric if (ignoreJump(SrcBlock, DstBlock, Jump))
9306f8fc217SDimitry Andric continue;
93177fc4c14SDimitry Andric LocalInDegree[Jump->Target]++;
93277fc4c14SDimitry Andric }
9336f8fc217SDimitry Andric };
9346f8fc217SDimitry Andric fillInDegree(SrcBlock);
935e3b55780SDimitry Andric for (auto *Block : UnknownBlocks) {
9366f8fc217SDimitry Andric fillInDegree(Block);
93777fc4c14SDimitry Andric }
93877fc4c14SDimitry Andric // A loop containing SrcBlock
93977fc4c14SDimitry Andric if (LocalInDegree[SrcBlock->Index] > 0)
94077fc4c14SDimitry Andric return false;
94177fc4c14SDimitry Andric
94277fc4c14SDimitry Andric std::vector<FlowBlock *> AcyclicOrder;
94377fc4c14SDimitry Andric std::queue<uint64_t> Queue;
94477fc4c14SDimitry Andric Queue.push(SrcBlock->Index);
94577fc4c14SDimitry Andric while (!Queue.empty()) {
9466f8fc217SDimitry Andric FlowBlock *Block = &Func.Blocks[Queue.front()];
94777fc4c14SDimitry Andric Queue.pop();
9486f8fc217SDimitry Andric // Stop propagation once we reach DstBlock, if any
9496f8fc217SDimitry Andric if (DstBlock != nullptr && Block == DstBlock)
95077fc4c14SDimitry Andric break;
95177fc4c14SDimitry Andric
9526f8fc217SDimitry Andric // Keep an acyclic order of unknown blocks
953e3b55780SDimitry Andric if (Block->HasUnknownWeight && Block != SrcBlock)
9546f8fc217SDimitry Andric AcyclicOrder.push_back(Block);
9556f8fc217SDimitry Andric
95677fc4c14SDimitry Andric // Add to the queue all successors with zero local in-degree
957e3b55780SDimitry Andric for (auto *Jump : Block->SuccJumps) {
9586f8fc217SDimitry Andric if (ignoreJump(SrcBlock, DstBlock, Jump))
9596f8fc217SDimitry Andric continue;
96077fc4c14SDimitry Andric uint64_t Dst = Jump->Target;
96177fc4c14SDimitry Andric LocalInDegree[Dst]--;
96277fc4c14SDimitry Andric if (LocalInDegree[Dst] == 0) {
96377fc4c14SDimitry Andric Queue.push(Dst);
96477fc4c14SDimitry Andric }
96577fc4c14SDimitry Andric }
96677fc4c14SDimitry Andric }
96777fc4c14SDimitry Andric
96877fc4c14SDimitry Andric // If there is a cycle in the subgraph, AcyclicOrder contains only a subset
96977fc4c14SDimitry Andric // of all blocks
9706f8fc217SDimitry Andric if (UnknownBlocks.size() != AcyclicOrder.size())
97177fc4c14SDimitry Andric return false;
9726f8fc217SDimitry Andric UnknownBlocks = AcyclicOrder;
97377fc4c14SDimitry Andric return true;
97477fc4c14SDimitry Andric }
97577fc4c14SDimitry Andric
9766f8fc217SDimitry Andric /// Rebalance a given subgraph rooted at SrcBlock, ending at DstBlock and
9776f8fc217SDimitry Andric /// having UnknownBlocks intermediate blocks.
rebalanceUnknownSubgraph(const FlowBlock * SrcBlock,const FlowBlock * DstBlock,const std::vector<FlowBlock * > & UnknownBlocks)9786f8fc217SDimitry Andric void rebalanceUnknownSubgraph(const FlowBlock *SrcBlock,
9796f8fc217SDimitry Andric const FlowBlock *DstBlock,
9806f8fc217SDimitry Andric const std::vector<FlowBlock *> &UnknownBlocks) {
98177fc4c14SDimitry Andric assert(SrcBlock->Flow > 0 && "zero-flow block in unknown subgraph");
98277fc4c14SDimitry Andric
9836f8fc217SDimitry Andric // Ditribute flow from the source block
9846f8fc217SDimitry Andric uint64_t BlockFlow = 0;
9856f8fc217SDimitry Andric // SrcBlock's flow is the sum of outgoing flows along non-ignored jumps
986e3b55780SDimitry Andric for (auto *Jump : SrcBlock->SuccJumps) {
9876f8fc217SDimitry Andric if (ignoreJump(SrcBlock, DstBlock, Jump))
98877fc4c14SDimitry Andric continue;
9896f8fc217SDimitry Andric BlockFlow += Jump->Flow;
99077fc4c14SDimitry Andric }
9916f8fc217SDimitry Andric rebalanceBlock(SrcBlock, DstBlock, SrcBlock, BlockFlow);
9926f8fc217SDimitry Andric
9936f8fc217SDimitry Andric // Ditribute flow from the remaining blocks
994e3b55780SDimitry Andric for (auto *Block : UnknownBlocks) {
995e3b55780SDimitry Andric assert(Block->HasUnknownWeight && "incorrect unknown subgraph");
9966f8fc217SDimitry Andric uint64_t BlockFlow = 0;
9976f8fc217SDimitry Andric // Block's flow is the sum of incoming flows
998e3b55780SDimitry Andric for (auto *Jump : Block->PredJumps) {
9996f8fc217SDimitry Andric BlockFlow += Jump->Flow;
10006f8fc217SDimitry Andric }
10016f8fc217SDimitry Andric Block->Flow = BlockFlow;
10026f8fc217SDimitry Andric rebalanceBlock(SrcBlock, DstBlock, Block, BlockFlow);
10036f8fc217SDimitry Andric }
10046f8fc217SDimitry Andric }
10056f8fc217SDimitry Andric
10066f8fc217SDimitry Andric /// Redistribute flow for a block in a subgraph rooted at SrcBlock,
10076f8fc217SDimitry Andric /// and ending at DstBlock.
rebalanceBlock(const FlowBlock * SrcBlock,const FlowBlock * DstBlock,const FlowBlock * Block,uint64_t BlockFlow)10086f8fc217SDimitry Andric void rebalanceBlock(const FlowBlock *SrcBlock, const FlowBlock *DstBlock,
10096f8fc217SDimitry Andric const FlowBlock *Block, uint64_t BlockFlow) {
10106f8fc217SDimitry Andric // Process all successor jumps and update corresponding flow values
10116f8fc217SDimitry Andric size_t BlockDegree = 0;
1012e3b55780SDimitry Andric for (auto *Jump : Block->SuccJumps) {
10136f8fc217SDimitry Andric if (ignoreJump(SrcBlock, DstBlock, Jump))
10146f8fc217SDimitry Andric continue;
10156f8fc217SDimitry Andric BlockDegree++;
10166f8fc217SDimitry Andric }
10176f8fc217SDimitry Andric // If all successor jumps of the block are ignored, skip it
10186f8fc217SDimitry Andric if (DstBlock == nullptr && BlockDegree == 0)
10196f8fc217SDimitry Andric return;
10206f8fc217SDimitry Andric assert(BlockDegree > 0 && "all outgoing jumps are ignored");
10216f8fc217SDimitry Andric
10226f8fc217SDimitry Andric // Each of the Block's successors gets the following amount of flow.
10236f8fc217SDimitry Andric // Rounding the value up so that all flow is propagated
10246f8fc217SDimitry Andric uint64_t SuccFlow = (BlockFlow + BlockDegree - 1) / BlockDegree;
1025e3b55780SDimitry Andric for (auto *Jump : Block->SuccJumps) {
10266f8fc217SDimitry Andric if (ignoreJump(SrcBlock, DstBlock, Jump))
10276f8fc217SDimitry Andric continue;
10286f8fc217SDimitry Andric uint64_t Flow = std::min(SuccFlow, BlockFlow);
102977fc4c14SDimitry Andric Jump->Flow = Flow;
10306f8fc217SDimitry Andric BlockFlow -= Flow;
103177fc4c14SDimitry Andric }
10326f8fc217SDimitry Andric assert(BlockFlow == 0 && "not all flow is propagated");
103377fc4c14SDimitry Andric }
103477fc4c14SDimitry Andric
103577fc4c14SDimitry Andric /// A constant indicating an arbitrary exit block of a function.
103677fc4c14SDimitry Andric static constexpr uint64_t AnyExitBlock = uint64_t(-1);
1037e3b55780SDimitry Andric /// Minimum BaseDistance for the jump distance values in island joining.
1038e3b55780SDimitry Andric static constexpr uint64_t MinBaseDistance = 10000;
103977fc4c14SDimitry Andric
1040e3b55780SDimitry Andric /// Params for flow computation.
1041e3b55780SDimitry Andric const ProfiParams &Params;
104277fc4c14SDimitry Andric /// The function.
104377fc4c14SDimitry Andric FlowFunction &Func;
104477fc4c14SDimitry Andric };
104577fc4c14SDimitry Andric
1046e3b55780SDimitry Andric std::pair<int64_t, int64_t> assignBlockCosts(const ProfiParams &Params,
1047e3b55780SDimitry Andric const FlowBlock &Block);
1048e3b55780SDimitry Andric std::pair<int64_t, int64_t> assignJumpCosts(const ProfiParams &Params,
1049e3b55780SDimitry Andric const FlowJump &Jump);
1050e3b55780SDimitry Andric
1051f65dcba8SDimitry Andric /// Initializing flow network for a given function.
1052f65dcba8SDimitry Andric ///
1053e3b55780SDimitry Andric /// Every block is split into two nodes that are responsible for (i) an
1054e3b55780SDimitry Andric /// incoming flow, (ii) an outgoing flow; they penalize an increase or a
1055f65dcba8SDimitry Andric /// reduction of the block weight.
initializeNetwork(const ProfiParams & Params,MinCostMaxFlow & Network,FlowFunction & Func)1056e3b55780SDimitry Andric void initializeNetwork(const ProfiParams &Params, MinCostMaxFlow &Network,
1057e3b55780SDimitry Andric FlowFunction &Func) {
1058f65dcba8SDimitry Andric uint64_t NumBlocks = Func.Blocks.size();
1059f65dcba8SDimitry Andric assert(NumBlocks > 1 && "Too few blocks in a function");
1060e3b55780SDimitry Andric uint64_t NumJumps = Func.Jumps.size();
1061e3b55780SDimitry Andric assert(NumJumps > 0 && "Too few jumps in a function");
1062f65dcba8SDimitry Andric
1063f65dcba8SDimitry Andric // Introducing dummy source/sink pairs to allow flow circulation.
1064ac9a064cSDimitry Andric // The nodes corresponding to blocks of the function have indices in
1065e3b55780SDimitry Andric // the range [0 .. 2 * NumBlocks); the dummy sources/sinks are indexed by the
1066e3b55780SDimitry Andric // next four values.
1067e3b55780SDimitry Andric uint64_t S = 2 * NumBlocks;
1068f65dcba8SDimitry Andric uint64_t T = S + 1;
1069f65dcba8SDimitry Andric uint64_t S1 = S + 2;
1070f65dcba8SDimitry Andric uint64_t T1 = S + 3;
1071f65dcba8SDimitry Andric
1072e3b55780SDimitry Andric Network.initialize(2 * NumBlocks + 4, S1, T1);
1073f65dcba8SDimitry Andric
1074e3b55780SDimitry Andric // Initialize nodes of the flow network
1075f65dcba8SDimitry Andric for (uint64_t B = 0; B < NumBlocks; B++) {
1076f65dcba8SDimitry Andric auto &Block = Func.Blocks[B];
1077f65dcba8SDimitry Andric
1078e3b55780SDimitry Andric // Split every block into two auxiliary nodes to allow
1079e3b55780SDimitry Andric // increase/reduction of the block count.
1080e3b55780SDimitry Andric uint64_t Bin = 2 * B;
1081e3b55780SDimitry Andric uint64_t Bout = 2 * B + 1;
1082f65dcba8SDimitry Andric
1083f65dcba8SDimitry Andric // Edges from S and to T
1084f65dcba8SDimitry Andric if (Block.isEntry()) {
1085f65dcba8SDimitry Andric Network.addEdge(S, Bin, 0);
1086f65dcba8SDimitry Andric } else if (Block.isExit()) {
1087f65dcba8SDimitry Andric Network.addEdge(Bout, T, 0);
1088f65dcba8SDimitry Andric }
1089f65dcba8SDimitry Andric
1090e3b55780SDimitry Andric // Assign costs for increasing/decreasing the block counts
1091e3b55780SDimitry Andric auto [AuxCostInc, AuxCostDec] = assignBlockCosts(Params, Block);
1092f65dcba8SDimitry Andric
1093e3b55780SDimitry Andric // Add the corresponding edges to the network
1094e3b55780SDimitry Andric Network.addEdge(Bin, Bout, AuxCostInc);
1095f65dcba8SDimitry Andric if (Block.Weight > 0) {
1096e3b55780SDimitry Andric Network.addEdge(Bout, Bin, Block.Weight, AuxCostDec);
1097e3b55780SDimitry Andric Network.addEdge(S1, Bout, Block.Weight, 0);
1098e3b55780SDimitry Andric Network.addEdge(Bin, T1, Block.Weight, 0);
1099f65dcba8SDimitry Andric }
1100f65dcba8SDimitry Andric }
1101f65dcba8SDimitry Andric
1102e3b55780SDimitry Andric // Initialize edges of the flow network
1103e3b55780SDimitry Andric for (uint64_t J = 0; J < NumJumps; J++) {
1104e3b55780SDimitry Andric auto &Jump = Func.Jumps[J];
1105e3b55780SDimitry Andric
1106e3b55780SDimitry Andric // Get the endpoints corresponding to the jump
1107e3b55780SDimitry Andric uint64_t Jin = 2 * Jump.Source + 1;
1108e3b55780SDimitry Andric uint64_t Jout = 2 * Jump.Target;
1109e3b55780SDimitry Andric
1110e3b55780SDimitry Andric // Assign costs for increasing/decreasing the jump counts
1111e3b55780SDimitry Andric auto [AuxCostInc, AuxCostDec] = assignJumpCosts(Params, Jump);
1112e3b55780SDimitry Andric
1113e3b55780SDimitry Andric // Add the corresponding edges to the network
1114e3b55780SDimitry Andric Network.addEdge(Jin, Jout, AuxCostInc);
1115e3b55780SDimitry Andric if (Jump.Weight > 0) {
1116e3b55780SDimitry Andric Network.addEdge(Jout, Jin, Jump.Weight, AuxCostDec);
1117e3b55780SDimitry Andric Network.addEdge(S1, Jout, Jump.Weight, 0);
1118e3b55780SDimitry Andric Network.addEdge(Jin, T1, Jump.Weight, 0);
1119f65dcba8SDimitry Andric }
1120f65dcba8SDimitry Andric }
1121f65dcba8SDimitry Andric
1122f65dcba8SDimitry Andric // Make sure we have a valid flow circulation
1123f65dcba8SDimitry Andric Network.addEdge(T, S, 0);
1124f65dcba8SDimitry Andric }
1125f65dcba8SDimitry Andric
1126e3b55780SDimitry Andric /// Assign costs for increasing/decreasing the block counts.
assignBlockCosts(const ProfiParams & Params,const FlowBlock & Block)1127e3b55780SDimitry Andric std::pair<int64_t, int64_t> assignBlockCosts(const ProfiParams &Params,
1128e3b55780SDimitry Andric const FlowBlock &Block) {
1129e3b55780SDimitry Andric // Modifying the weight of an unlikely block is expensive
1130e3b55780SDimitry Andric if (Block.IsUnlikely)
1131e3b55780SDimitry Andric return std::make_pair(Params.CostUnlikely, Params.CostUnlikely);
1132f65dcba8SDimitry Andric
1133e3b55780SDimitry Andric // Assign default values for the costs
1134e3b55780SDimitry Andric int64_t CostInc = Params.CostBlockInc;
1135e3b55780SDimitry Andric int64_t CostDec = Params.CostBlockDec;
1136e3b55780SDimitry Andric // Update the costs depending on the block metadata
1137e3b55780SDimitry Andric if (Block.HasUnknownWeight) {
1138e3b55780SDimitry Andric CostInc = Params.CostBlockUnknownInc;
1139e3b55780SDimitry Andric CostDec = 0;
1140e3b55780SDimitry Andric } else {
1141e3b55780SDimitry Andric // Increasing the count for "cold" blocks with zero initial count is more
1142e3b55780SDimitry Andric // expensive than for "hot" ones
1143e3b55780SDimitry Andric if (Block.Weight == 0)
1144e3b55780SDimitry Andric CostInc = Params.CostBlockZeroInc;
1145e3b55780SDimitry Andric // Modifying the count of the entry block is expensive
1146e3b55780SDimitry Andric if (Block.isEntry()) {
1147e3b55780SDimitry Andric CostInc = Params.CostBlockEntryInc;
1148e3b55780SDimitry Andric CostDec = Params.CostBlockEntryDec;
1149f65dcba8SDimitry Andric }
1150f65dcba8SDimitry Andric }
1151e3b55780SDimitry Andric return std::make_pair(CostInc, CostDec);
1152f65dcba8SDimitry Andric }
1153f65dcba8SDimitry Andric
1154e3b55780SDimitry Andric /// Assign costs for increasing/decreasing the jump counts.
assignJumpCosts(const ProfiParams & Params,const FlowJump & Jump)1155e3b55780SDimitry Andric std::pair<int64_t, int64_t> assignJumpCosts(const ProfiParams &Params,
1156e3b55780SDimitry Andric const FlowJump &Jump) {
1157e3b55780SDimitry Andric // Modifying the weight of an unlikely jump is expensive
1158e3b55780SDimitry Andric if (Jump.IsUnlikely)
1159e3b55780SDimitry Andric return std::make_pair(Params.CostUnlikely, Params.CostUnlikely);
1160e3b55780SDimitry Andric
1161e3b55780SDimitry Andric // Assign default values for the costs
1162e3b55780SDimitry Andric int64_t CostInc = Params.CostJumpInc;
1163e3b55780SDimitry Andric int64_t CostDec = Params.CostJumpDec;
1164e3b55780SDimitry Andric // Update the costs depending on the block metadata
1165e3b55780SDimitry Andric if (Jump.Source + 1 == Jump.Target) {
1166e3b55780SDimitry Andric // Adjusting the fall-through branch
1167e3b55780SDimitry Andric CostInc = Params.CostJumpFTInc;
1168e3b55780SDimitry Andric CostDec = Params.CostJumpFTDec;
1169e3b55780SDimitry Andric }
1170e3b55780SDimitry Andric if (Jump.HasUnknownWeight) {
1171e3b55780SDimitry Andric // The cost is different for fall-through and non-fall-through branches
1172e3b55780SDimitry Andric if (Jump.Source + 1 == Jump.Target)
1173e3b55780SDimitry Andric CostInc = Params.CostJumpUnknownFTInc;
1174e3b55780SDimitry Andric else
1175e3b55780SDimitry Andric CostInc = Params.CostJumpUnknownInc;
1176e3b55780SDimitry Andric CostDec = 0;
1177e3b55780SDimitry Andric } else {
1178e3b55780SDimitry Andric assert(Jump.Weight > 0 && "found zero-weight jump with a positive weight");
1179e3b55780SDimitry Andric }
1180e3b55780SDimitry Andric return std::make_pair(CostInc, CostDec);
1181e3b55780SDimitry Andric }
1182e3b55780SDimitry Andric
1183e3b55780SDimitry Andric /// Extract resulting block and edge counts from the flow network.
extractWeights(const ProfiParams & Params,MinCostMaxFlow & Network,FlowFunction & Func)1184e3b55780SDimitry Andric void extractWeights(const ProfiParams &Params, MinCostMaxFlow &Network,
1185e3b55780SDimitry Andric FlowFunction &Func) {
1186e3b55780SDimitry Andric uint64_t NumBlocks = Func.Blocks.size();
1187e3b55780SDimitry Andric uint64_t NumJumps = Func.Jumps.size();
1188e3b55780SDimitry Andric
1189f65dcba8SDimitry Andric // Extract resulting jump counts
1190e3b55780SDimitry Andric for (uint64_t J = 0; J < NumJumps; J++) {
1191e3b55780SDimitry Andric auto &Jump = Func.Jumps[J];
1192e3b55780SDimitry Andric uint64_t SrcOut = 2 * Jump.Source + 1;
1193e3b55780SDimitry Andric uint64_t DstIn = 2 * Jump.Target;
1194e3b55780SDimitry Andric
1195f65dcba8SDimitry Andric int64_t Flow = 0;
1196e3b55780SDimitry Andric int64_t AuxFlow = Network.getFlow(SrcOut, DstIn);
1197e3b55780SDimitry Andric if (Jump.Source != Jump.Target)
1198e3b55780SDimitry Andric Flow = int64_t(Jump.Weight) + AuxFlow;
1199e3b55780SDimitry Andric else
1200e3b55780SDimitry Andric Flow = int64_t(Jump.Weight) + (AuxFlow > 0 ? AuxFlow : 0);
1201e3b55780SDimitry Andric
1202f65dcba8SDimitry Andric Jump.Flow = Flow;
1203f65dcba8SDimitry Andric assert(Flow >= 0 && "negative jump flow");
1204f65dcba8SDimitry Andric }
1205e3b55780SDimitry Andric
1206e3b55780SDimitry Andric // Extract resulting block counts
1207e3b55780SDimitry Andric auto InFlow = std::vector<uint64_t>(NumBlocks, 0);
1208e3b55780SDimitry Andric auto OutFlow = std::vector<uint64_t>(NumBlocks, 0);
1209e3b55780SDimitry Andric for (auto &Jump : Func.Jumps) {
1210e3b55780SDimitry Andric InFlow[Jump.Target] += Jump.Flow;
1211e3b55780SDimitry Andric OutFlow[Jump.Source] += Jump.Flow;
1212e3b55780SDimitry Andric }
1213e3b55780SDimitry Andric for (uint64_t B = 0; B < NumBlocks; B++) {
1214e3b55780SDimitry Andric auto &Block = Func.Blocks[B];
1215e3b55780SDimitry Andric Block.Flow = std::max(OutFlow[B], InFlow[B]);
1216e3b55780SDimitry Andric }
1217f65dcba8SDimitry Andric }
1218f65dcba8SDimitry Andric
1219f65dcba8SDimitry Andric #ifndef NDEBUG
1220e3b55780SDimitry Andric /// Verify that the provided block/jump weights are as expected.
verifyInput(const FlowFunction & Func)1221e3b55780SDimitry Andric void verifyInput(const FlowFunction &Func) {
12227fa27ce4SDimitry Andric // Verify entry and exit blocks
1223e3b55780SDimitry Andric assert(Func.Entry == 0 && Func.Blocks[0].isEntry());
12247fa27ce4SDimitry Andric size_t NumExitBlocks = 0;
1225e3b55780SDimitry Andric for (size_t I = 1; I < Func.Blocks.size(); I++) {
1226e3b55780SDimitry Andric assert(!Func.Blocks[I].isEntry() && "multiple entry blocks");
12277fa27ce4SDimitry Andric if (Func.Blocks[I].isExit())
12287fa27ce4SDimitry Andric NumExitBlocks++;
12297fa27ce4SDimitry Andric }
12307fa27ce4SDimitry Andric assert(NumExitBlocks > 0 && "cannot find exit blocks");
12317fa27ce4SDimitry Andric
12327fa27ce4SDimitry Andric // Verify that there are no parallel edges
12337fa27ce4SDimitry Andric for (auto &Block : Func.Blocks) {
12347fa27ce4SDimitry Andric std::unordered_set<uint64_t> UniqueSuccs;
12357fa27ce4SDimitry Andric for (auto &Jump : Block.SuccJumps) {
12367fa27ce4SDimitry Andric auto It = UniqueSuccs.insert(Jump->Target);
12377fa27ce4SDimitry Andric assert(It.second && "input CFG contains parallel edges");
12387fa27ce4SDimitry Andric }
1239e3b55780SDimitry Andric }
1240e3b55780SDimitry Andric // Verify CFG jumps
1241e3b55780SDimitry Andric for (auto &Block : Func.Blocks) {
1242e3b55780SDimitry Andric assert((!Block.isEntry() || !Block.isExit()) &&
1243e3b55780SDimitry Andric "a block cannot be an entry and an exit");
1244e3b55780SDimitry Andric }
1245e3b55780SDimitry Andric // Verify input block weights
1246e3b55780SDimitry Andric for (auto &Block : Func.Blocks) {
1247e3b55780SDimitry Andric assert((!Block.HasUnknownWeight || Block.Weight == 0 || Block.isEntry()) &&
1248e3b55780SDimitry Andric "non-zero weight of a block w/o weight except for an entry");
1249e3b55780SDimitry Andric }
1250e3b55780SDimitry Andric // Verify input jump weights
1251e3b55780SDimitry Andric for (auto &Jump : Func.Jumps) {
1252e3b55780SDimitry Andric assert((!Jump.HasUnknownWeight || Jump.Weight == 0) &&
1253e3b55780SDimitry Andric "non-zero weight of a jump w/o weight");
1254e3b55780SDimitry Andric }
1255e3b55780SDimitry Andric }
1256e3b55780SDimitry Andric
1257e3b55780SDimitry Andric /// Verify that the computed flow values satisfy flow conservation rules.
verifyOutput(const FlowFunction & Func)1258e3b55780SDimitry Andric void verifyOutput(const FlowFunction &Func) {
1259f65dcba8SDimitry Andric const uint64_t NumBlocks = Func.Blocks.size();
1260f65dcba8SDimitry Andric auto InFlow = std::vector<uint64_t>(NumBlocks, 0);
1261f65dcba8SDimitry Andric auto OutFlow = std::vector<uint64_t>(NumBlocks, 0);
1262e3b55780SDimitry Andric for (const auto &Jump : Func.Jumps) {
1263f65dcba8SDimitry Andric InFlow[Jump.Target] += Jump.Flow;
1264f65dcba8SDimitry Andric OutFlow[Jump.Source] += Jump.Flow;
1265f65dcba8SDimitry Andric }
1266f65dcba8SDimitry Andric
1267f65dcba8SDimitry Andric uint64_t TotalInFlow = 0;
1268f65dcba8SDimitry Andric uint64_t TotalOutFlow = 0;
1269f65dcba8SDimitry Andric for (uint64_t I = 0; I < NumBlocks; I++) {
1270f65dcba8SDimitry Andric auto &Block = Func.Blocks[I];
1271f65dcba8SDimitry Andric if (Block.isEntry()) {
1272f65dcba8SDimitry Andric TotalInFlow += Block.Flow;
1273f65dcba8SDimitry Andric assert(Block.Flow == OutFlow[I] && "incorrectly computed control flow");
1274f65dcba8SDimitry Andric } else if (Block.isExit()) {
1275f65dcba8SDimitry Andric TotalOutFlow += Block.Flow;
1276f65dcba8SDimitry Andric assert(Block.Flow == InFlow[I] && "incorrectly computed control flow");
1277f65dcba8SDimitry Andric } else {
1278f65dcba8SDimitry Andric assert(Block.Flow == OutFlow[I] && "incorrectly computed control flow");
1279f65dcba8SDimitry Andric assert(Block.Flow == InFlow[I] && "incorrectly computed control flow");
1280f65dcba8SDimitry Andric }
1281f65dcba8SDimitry Andric }
1282f65dcba8SDimitry Andric assert(TotalInFlow == TotalOutFlow && "incorrectly computed control flow");
128377fc4c14SDimitry Andric
128477fc4c14SDimitry Andric // Verify that there are no isolated flow components
128577fc4c14SDimitry Andric // One could modify FlowFunction to hold edges indexed by the sources, which
128677fc4c14SDimitry Andric // will avoid a creation of the object
128777fc4c14SDimitry Andric auto PositiveFlowEdges = std::vector<std::vector<uint64_t>>(NumBlocks);
1288e3b55780SDimitry Andric for (const auto &Jump : Func.Jumps) {
128977fc4c14SDimitry Andric if (Jump.Flow > 0) {
129077fc4c14SDimitry Andric PositiveFlowEdges[Jump.Source].push_back(Jump.Target);
129177fc4c14SDimitry Andric }
129277fc4c14SDimitry Andric }
129377fc4c14SDimitry Andric
129477fc4c14SDimitry Andric // Run BFS from the source along edges with positive flow
129577fc4c14SDimitry Andric std::queue<uint64_t> Queue;
12966f8fc217SDimitry Andric auto Visited = BitVector(NumBlocks, false);
129777fc4c14SDimitry Andric Queue.push(Func.Entry);
129877fc4c14SDimitry Andric Visited[Func.Entry] = true;
129977fc4c14SDimitry Andric while (!Queue.empty()) {
130077fc4c14SDimitry Andric uint64_t Src = Queue.front();
130177fc4c14SDimitry Andric Queue.pop();
130277fc4c14SDimitry Andric for (uint64_t Dst : PositiveFlowEdges[Src]) {
130377fc4c14SDimitry Andric if (!Visited[Dst]) {
130477fc4c14SDimitry Andric Queue.push(Dst);
130577fc4c14SDimitry Andric Visited[Dst] = true;
130677fc4c14SDimitry Andric }
130777fc4c14SDimitry Andric }
130877fc4c14SDimitry Andric }
130977fc4c14SDimitry Andric
131077fc4c14SDimitry Andric // Verify that every block that has a positive flow is reached from the source
131177fc4c14SDimitry Andric // along edges with a positive flow
131277fc4c14SDimitry Andric for (uint64_t I = 0; I < NumBlocks; I++) {
131377fc4c14SDimitry Andric auto &Block = Func.Blocks[I];
131477fc4c14SDimitry Andric assert((Visited[I] || Block.Flow == 0) && "an isolated flow component");
131577fc4c14SDimitry Andric }
1316f65dcba8SDimitry Andric }
1317f65dcba8SDimitry Andric #endif
1318f65dcba8SDimitry Andric
1319f65dcba8SDimitry Andric } // end of anonymous namespace
1320f65dcba8SDimitry Andric
13217fa27ce4SDimitry Andric /// Apply the profile inference algorithm for a given function and provided
13227fa27ce4SDimitry Andric /// profi options
applyFlowInference(const ProfiParams & Params,FlowFunction & Func)1323e3b55780SDimitry Andric void llvm::applyFlowInference(const ProfiParams &Params, FlowFunction &Func) {
13247fa27ce4SDimitry Andric // Check if the function has samples and assign initial flow values
13257fa27ce4SDimitry Andric bool HasSamples = false;
13267fa27ce4SDimitry Andric for (FlowBlock &Block : Func.Blocks) {
13277fa27ce4SDimitry Andric if (Block.Weight > 0)
13287fa27ce4SDimitry Andric HasSamples = true;
13297fa27ce4SDimitry Andric Block.Flow = Block.Weight;
13307fa27ce4SDimitry Andric }
13317fa27ce4SDimitry Andric for (FlowJump &Jump : Func.Jumps) {
13327fa27ce4SDimitry Andric if (Jump.Weight > 0)
13337fa27ce4SDimitry Andric HasSamples = true;
13347fa27ce4SDimitry Andric Jump.Flow = Jump.Weight;
13357fa27ce4SDimitry Andric }
13367fa27ce4SDimitry Andric
13377fa27ce4SDimitry Andric // Quit early for functions with a single block or ones w/o samples
13387fa27ce4SDimitry Andric if (Func.Blocks.size() <= 1 || !HasSamples)
13397fa27ce4SDimitry Andric return;
13407fa27ce4SDimitry Andric
1341e3b55780SDimitry Andric #ifndef NDEBUG
1342e3b55780SDimitry Andric // Verify the input data
1343e3b55780SDimitry Andric verifyInput(Func);
1344e3b55780SDimitry Andric #endif
1345e3b55780SDimitry Andric
1346f65dcba8SDimitry Andric // Create and apply an inference network model
1347e3b55780SDimitry Andric auto InferenceNetwork = MinCostMaxFlow(Params);
1348e3b55780SDimitry Andric initializeNetwork(Params, InferenceNetwork, Func);
1349f65dcba8SDimitry Andric InferenceNetwork.run();
1350f65dcba8SDimitry Andric
1351f65dcba8SDimitry Andric // Extract flow values for every block and every edge
1352e3b55780SDimitry Andric extractWeights(Params, InferenceNetwork, Func);
1353f65dcba8SDimitry Andric
135477fc4c14SDimitry Andric // Post-processing adjustments to the flow
1355e3b55780SDimitry Andric auto Adjuster = FlowAdjuster(Params, Func);
135677fc4c14SDimitry Andric Adjuster.run();
135777fc4c14SDimitry Andric
1358f65dcba8SDimitry Andric #ifndef NDEBUG
1359f65dcba8SDimitry Andric // Verify the result
1360e3b55780SDimitry Andric verifyOutput(Func);
1361f65dcba8SDimitry Andric #endif
1362f65dcba8SDimitry Andric }
1363e3b55780SDimitry Andric
1364e3b55780SDimitry Andric /// Apply the profile inference algorithm for a given flow function
applyFlowInference(FlowFunction & Func)1365e3b55780SDimitry Andric void llvm::applyFlowInference(FlowFunction &Func) {
1366e3b55780SDimitry Andric ProfiParams Params;
1367e3b55780SDimitry Andric // Set the params from the command-line flags.
1368e3b55780SDimitry Andric Params.EvenFlowDistribution = SampleProfileEvenFlowDistribution;
1369e3b55780SDimitry Andric Params.RebalanceUnknown = SampleProfileRebalanceUnknown;
1370e3b55780SDimitry Andric Params.JoinIslands = SampleProfileJoinIslands;
1371e3b55780SDimitry Andric Params.CostBlockInc = SampleProfileProfiCostBlockInc;
1372e3b55780SDimitry Andric Params.CostBlockDec = SampleProfileProfiCostBlockDec;
1373e3b55780SDimitry Andric Params.CostBlockEntryInc = SampleProfileProfiCostBlockEntryInc;
1374e3b55780SDimitry Andric Params.CostBlockEntryDec = SampleProfileProfiCostBlockEntryDec;
1375e3b55780SDimitry Andric Params.CostBlockZeroInc = SampleProfileProfiCostBlockZeroInc;
1376e3b55780SDimitry Andric Params.CostBlockUnknownInc = SampleProfileProfiCostBlockUnknownInc;
1377e3b55780SDimitry Andric
1378e3b55780SDimitry Andric applyFlowInference(Params, Func);
1379e3b55780SDimitry Andric }
1380