xref: /freebsd-src/contrib/llvm-project/llvm/lib/Transforms/Utils/SampleProfileInference.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
14824e7fdSDimitry Andric //===- SampleProfileInference.cpp - Adjust sample profiles in the IR ------===//
24824e7fdSDimitry Andric //
34824e7fdSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44824e7fdSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
54824e7fdSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
64824e7fdSDimitry Andric //
74824e7fdSDimitry Andric //===----------------------------------------------------------------------===//
84824e7fdSDimitry Andric //
94824e7fdSDimitry Andric // This file implements a profile inference algorithm. Given an incomplete and
104824e7fdSDimitry Andric // possibly imprecise block counts, the algorithm reconstructs realistic block
114824e7fdSDimitry Andric // and edge counts that satisfy flow conservation rules, while minimally modify
124824e7fdSDimitry Andric // input block counts.
134824e7fdSDimitry Andric //
144824e7fdSDimitry Andric //===----------------------------------------------------------------------===//
154824e7fdSDimitry Andric 
164824e7fdSDimitry Andric #include "llvm/Transforms/Utils/SampleProfileInference.h"
1704eeddc0SDimitry Andric #include "llvm/ADT/BitVector.h"
1881ad6265SDimitry Andric #include "llvm/Support/CommandLine.h"
194824e7fdSDimitry Andric #include "llvm/Support/Debug.h"
204824e7fdSDimitry Andric #include <queue>
214824e7fdSDimitry Andric #include <set>
2281ad6265SDimitry Andric #include <stack>
2306c3fb27SDimitry Andric #include <unordered_set>
244824e7fdSDimitry Andric 
254824e7fdSDimitry Andric using namespace llvm;
264824e7fdSDimitry Andric #define DEBUG_TYPE "sample-profile-inference"
274824e7fdSDimitry Andric 
284824e7fdSDimitry Andric namespace {
294824e7fdSDimitry Andric 
30bdd1243dSDimitry Andric static cl::opt<bool> SampleProfileEvenFlowDistribution(
31bdd1243dSDimitry Andric     "sample-profile-even-flow-distribution", cl::init(true), cl::Hidden,
32bdd1243dSDimitry Andric     cl::desc("Try to evenly distribute flow when there are multiple equally "
3381ad6265SDimitry Andric              "likely options."));
3481ad6265SDimitry Andric 
35bdd1243dSDimitry Andric static cl::opt<bool> SampleProfileRebalanceUnknown(
36bdd1243dSDimitry Andric     "sample-profile-rebalance-unknown", cl::init(true), cl::Hidden,
37bdd1243dSDimitry Andric     cl::desc("Evenly re-distribute flow among unknown subgraphs."));
3881ad6265SDimitry Andric 
39bdd1243dSDimitry Andric static cl::opt<bool> SampleProfileJoinIslands(
40bdd1243dSDimitry Andric     "sample-profile-join-islands", cl::init(true), cl::Hidden,
41bdd1243dSDimitry Andric     cl::desc("Join isolated components having positive flow."));
4281ad6265SDimitry Andric 
43bdd1243dSDimitry Andric static cl::opt<unsigned> SampleProfileProfiCostBlockInc(
44bdd1243dSDimitry Andric     "sample-profile-profi-cost-block-inc", cl::init(10), cl::Hidden,
45bdd1243dSDimitry Andric     cl::desc("The cost of increasing a block's count by one."));
4681ad6265SDimitry Andric 
47bdd1243dSDimitry Andric static cl::opt<unsigned> SampleProfileProfiCostBlockDec(
48bdd1243dSDimitry Andric     "sample-profile-profi-cost-block-dec", cl::init(20), cl::Hidden,
49bdd1243dSDimitry Andric     cl::desc("The cost of decreasing a block's count by one."));
5081ad6265SDimitry Andric 
51bdd1243dSDimitry Andric static cl::opt<unsigned> SampleProfileProfiCostBlockEntryInc(
52bdd1243dSDimitry Andric     "sample-profile-profi-cost-block-entry-inc", cl::init(40), cl::Hidden,
53bdd1243dSDimitry Andric     cl::desc("The cost of increasing the entry block's count by one."));
5481ad6265SDimitry Andric 
55bdd1243dSDimitry Andric static cl::opt<unsigned> SampleProfileProfiCostBlockEntryDec(
56bdd1243dSDimitry Andric     "sample-profile-profi-cost-block-entry-dec", cl::init(10), cl::Hidden,
57bdd1243dSDimitry Andric     cl::desc("The cost of decreasing the entry block's count by one."));
58bdd1243dSDimitry Andric 
59bdd1243dSDimitry Andric static cl::opt<unsigned> SampleProfileProfiCostBlockZeroInc(
60bdd1243dSDimitry Andric     "sample-profile-profi-cost-block-zero-inc", cl::init(11), cl::Hidden,
61bdd1243dSDimitry Andric     cl::desc("The cost of increasing a count of zero-weight block by one."));
62bdd1243dSDimitry Andric 
63bdd1243dSDimitry Andric static cl::opt<unsigned> SampleProfileProfiCostBlockUnknownInc(
64bdd1243dSDimitry Andric     "sample-profile-profi-cost-block-unknown-inc", cl::init(0), cl::Hidden,
65bdd1243dSDimitry Andric     cl::desc("The cost of increasing an unknown block's count by one."));
6681ad6265SDimitry Andric 
674824e7fdSDimitry Andric /// A value indicating an infinite flow/capacity/weight of a block/edge.
684824e7fdSDimitry Andric /// Not using numeric_limits<int64_t>::max(), as the values can be summed up
694824e7fdSDimitry Andric /// during the execution.
704824e7fdSDimitry Andric static constexpr int64_t INF = ((int64_t)1) << 50;
714824e7fdSDimitry Andric 
724824e7fdSDimitry Andric /// The minimum-cost maximum flow algorithm.
734824e7fdSDimitry Andric ///
744824e7fdSDimitry Andric /// The algorithm finds the maximum flow of minimum cost on a given (directed)
754824e7fdSDimitry Andric /// network using a modified version of the classical Moore-Bellman-Ford
764824e7fdSDimitry Andric /// approach. The algorithm applies a number of augmentation iterations in which
774824e7fdSDimitry Andric /// flow is sent along paths of positive capacity from the source to the sink.
784824e7fdSDimitry Andric /// The worst-case time complexity of the implementation is O(v(f)*m*n), where
794824e7fdSDimitry Andric /// where m is the number of edges, n is the number of vertices, and v(f) is the
804824e7fdSDimitry Andric /// value of the maximum flow. However, the observed running time on typical
814824e7fdSDimitry Andric /// instances is sub-quadratic, that is, o(n^2).
824824e7fdSDimitry Andric ///
834824e7fdSDimitry Andric /// The input is a set of edges with specified costs and capacities, and a pair
844824e7fdSDimitry Andric /// of nodes (source and sink). The output is the flow along each edge of the
854824e7fdSDimitry Andric /// minimum total cost respecting the given edge capacities.
864824e7fdSDimitry Andric class MinCostMaxFlow {
874824e7fdSDimitry Andric public:
88bdd1243dSDimitry Andric   MinCostMaxFlow(const ProfiParams &Params) : Params(Params) {}
89bdd1243dSDimitry Andric 
904824e7fdSDimitry Andric   // Initialize algorithm's data structures for a network of a given size.
914824e7fdSDimitry Andric   void initialize(uint64_t NodeCount, uint64_t SourceNode, uint64_t SinkNode) {
924824e7fdSDimitry Andric     Source = SourceNode;
934824e7fdSDimitry Andric     Target = SinkNode;
944824e7fdSDimitry Andric 
954824e7fdSDimitry Andric     Nodes = std::vector<Node>(NodeCount);
964824e7fdSDimitry Andric     Edges = std::vector<std::vector<Edge>>(NodeCount, std::vector<Edge>());
97bdd1243dSDimitry Andric     if (Params.EvenFlowDistribution)
9881ad6265SDimitry Andric       AugmentingEdges =
9981ad6265SDimitry Andric           std::vector<std::vector<Edge *>>(NodeCount, std::vector<Edge *>());
1004824e7fdSDimitry Andric   }
1014824e7fdSDimitry Andric 
1024824e7fdSDimitry Andric   // Run the algorithm.
1034824e7fdSDimitry Andric   int64_t run() {
104bdd1243dSDimitry Andric     LLVM_DEBUG(dbgs() << "Starting profi for " << Nodes.size() << " nodes\n");
105bdd1243dSDimitry Andric 
10681ad6265SDimitry Andric     // Iteratively find an augmentation path/dag in the network and send the
10781ad6265SDimitry Andric     // flow along its edges
10881ad6265SDimitry Andric     size_t AugmentationIters = applyFlowAugmentation();
1094824e7fdSDimitry Andric 
1104824e7fdSDimitry Andric     // Compute the total flow and its cost
1114824e7fdSDimitry Andric     int64_t TotalCost = 0;
1124824e7fdSDimitry Andric     int64_t TotalFlow = 0;
1134824e7fdSDimitry Andric     for (uint64_t Src = 0; Src < Nodes.size(); Src++) {
1144824e7fdSDimitry Andric       for (auto &Edge : Edges[Src]) {
1154824e7fdSDimitry Andric         if (Edge.Flow > 0) {
1164824e7fdSDimitry Andric           TotalCost += Edge.Cost * Edge.Flow;
1174824e7fdSDimitry Andric           if (Src == Source)
1184824e7fdSDimitry Andric             TotalFlow += Edge.Flow;
1194824e7fdSDimitry Andric         }
1204824e7fdSDimitry Andric       }
1214824e7fdSDimitry Andric     }
1224824e7fdSDimitry Andric     LLVM_DEBUG(dbgs() << "Completed profi after " << AugmentationIters
1234824e7fdSDimitry Andric                       << " iterations with " << TotalFlow << " total flow"
1244824e7fdSDimitry Andric                       << " of " << TotalCost << " cost\n");
1254824e7fdSDimitry Andric     (void)TotalFlow;
12681ad6265SDimitry Andric     (void)AugmentationIters;
1274824e7fdSDimitry Andric     return TotalCost;
1284824e7fdSDimitry Andric   }
1294824e7fdSDimitry Andric 
1304824e7fdSDimitry Andric   /// Adding an edge to the network with a specified capacity and a cost.
1314824e7fdSDimitry Andric   /// Multiple edges between a pair of nodes are allowed but self-edges
1324824e7fdSDimitry Andric   /// are not supported.
1334824e7fdSDimitry Andric   void addEdge(uint64_t Src, uint64_t Dst, int64_t Capacity, int64_t Cost) {
1344824e7fdSDimitry Andric     assert(Capacity > 0 && "adding an edge of zero capacity");
1354824e7fdSDimitry Andric     assert(Src != Dst && "loop edge are not supported");
1364824e7fdSDimitry Andric 
1374824e7fdSDimitry Andric     Edge SrcEdge;
1384824e7fdSDimitry Andric     SrcEdge.Dst = Dst;
1394824e7fdSDimitry Andric     SrcEdge.Cost = Cost;
1404824e7fdSDimitry Andric     SrcEdge.Capacity = Capacity;
1414824e7fdSDimitry Andric     SrcEdge.Flow = 0;
1424824e7fdSDimitry Andric     SrcEdge.RevEdgeIndex = Edges[Dst].size();
1434824e7fdSDimitry Andric 
1444824e7fdSDimitry Andric     Edge DstEdge;
1454824e7fdSDimitry Andric     DstEdge.Dst = Src;
1464824e7fdSDimitry Andric     DstEdge.Cost = -Cost;
1474824e7fdSDimitry Andric     DstEdge.Capacity = 0;
1484824e7fdSDimitry Andric     DstEdge.Flow = 0;
1494824e7fdSDimitry Andric     DstEdge.RevEdgeIndex = Edges[Src].size();
1504824e7fdSDimitry Andric 
1514824e7fdSDimitry Andric     Edges[Src].push_back(SrcEdge);
1524824e7fdSDimitry Andric     Edges[Dst].push_back(DstEdge);
1534824e7fdSDimitry Andric   }
1544824e7fdSDimitry Andric 
1554824e7fdSDimitry Andric   /// Adding an edge to the network of infinite capacity and a given cost.
1564824e7fdSDimitry Andric   void addEdge(uint64_t Src, uint64_t Dst, int64_t Cost) {
1574824e7fdSDimitry Andric     addEdge(Src, Dst, INF, Cost);
1584824e7fdSDimitry Andric   }
1594824e7fdSDimitry Andric 
1604824e7fdSDimitry Andric   /// Get the total flow from a given source node.
1614824e7fdSDimitry Andric   /// Returns a list of pairs (target node, amount of flow to the target).
1625f757f3fSDimitry Andric   std::vector<std::pair<uint64_t, int64_t>> getFlow(uint64_t Src) const {
1634824e7fdSDimitry Andric     std::vector<std::pair<uint64_t, int64_t>> Flow;
164bdd1243dSDimitry Andric     for (const auto &Edge : Edges[Src]) {
1654824e7fdSDimitry Andric       if (Edge.Flow > 0)
1664824e7fdSDimitry Andric         Flow.push_back(std::make_pair(Edge.Dst, Edge.Flow));
1674824e7fdSDimitry Andric     }
1684824e7fdSDimitry Andric     return Flow;
1694824e7fdSDimitry Andric   }
1704824e7fdSDimitry Andric 
1714824e7fdSDimitry Andric   /// Get the total flow between a pair of nodes.
1724824e7fdSDimitry Andric   int64_t getFlow(uint64_t Src, uint64_t Dst) const {
1734824e7fdSDimitry Andric     int64_t Flow = 0;
174bdd1243dSDimitry Andric     for (const auto &Edge : Edges[Src]) {
1754824e7fdSDimitry Andric       if (Edge.Dst == Dst) {
1764824e7fdSDimitry Andric         Flow += Edge.Flow;
1774824e7fdSDimitry Andric       }
1784824e7fdSDimitry Andric     }
1794824e7fdSDimitry Andric     return Flow;
1804824e7fdSDimitry Andric   }
1814824e7fdSDimitry Andric 
1824824e7fdSDimitry Andric private:
18381ad6265SDimitry Andric   /// Iteratively find an augmentation path/dag in the network and send the
18481ad6265SDimitry Andric   /// flow along its edges. The method returns the number of applied iterations.
18581ad6265SDimitry Andric   size_t applyFlowAugmentation() {
18681ad6265SDimitry Andric     size_t AugmentationIters = 0;
18781ad6265SDimitry Andric     while (findAugmentingPath()) {
18881ad6265SDimitry Andric       uint64_t PathCapacity = computeAugmentingPathCapacity();
18981ad6265SDimitry Andric       while (PathCapacity > 0) {
19081ad6265SDimitry Andric         bool Progress = false;
191bdd1243dSDimitry Andric         if (Params.EvenFlowDistribution) {
19281ad6265SDimitry Andric           // Identify node/edge candidates for augmentation
19381ad6265SDimitry Andric           identifyShortestEdges(PathCapacity);
19481ad6265SDimitry Andric 
19581ad6265SDimitry Andric           // Find an augmenting DAG
19681ad6265SDimitry Andric           auto AugmentingOrder = findAugmentingDAG();
19781ad6265SDimitry Andric 
19881ad6265SDimitry Andric           // Apply the DAG augmentation
19981ad6265SDimitry Andric           Progress = augmentFlowAlongDAG(AugmentingOrder);
20081ad6265SDimitry Andric           PathCapacity = computeAugmentingPathCapacity();
20181ad6265SDimitry Andric         }
20281ad6265SDimitry Andric 
20381ad6265SDimitry Andric         if (!Progress) {
20481ad6265SDimitry Andric           augmentFlowAlongPath(PathCapacity);
20581ad6265SDimitry Andric           PathCapacity = 0;
20681ad6265SDimitry Andric         }
20781ad6265SDimitry Andric 
20881ad6265SDimitry Andric         AugmentationIters++;
20981ad6265SDimitry Andric       }
21081ad6265SDimitry Andric     }
21181ad6265SDimitry Andric     return AugmentationIters;
21281ad6265SDimitry Andric   }
21381ad6265SDimitry Andric 
21481ad6265SDimitry Andric   /// Compute the capacity of the cannonical augmenting path. If the path is
21581ad6265SDimitry Andric   /// saturated (that is, no flow can be sent along the path), then return 0.
21681ad6265SDimitry Andric   uint64_t computeAugmentingPathCapacity() {
21781ad6265SDimitry Andric     uint64_t PathCapacity = INF;
21881ad6265SDimitry Andric     uint64_t Now = Target;
21981ad6265SDimitry Andric     while (Now != Source) {
22081ad6265SDimitry Andric       uint64_t Pred = Nodes[Now].ParentNode;
22181ad6265SDimitry Andric       auto &Edge = Edges[Pred][Nodes[Now].ParentEdgeIndex];
22281ad6265SDimitry Andric 
22381ad6265SDimitry Andric       assert(Edge.Capacity >= Edge.Flow && "incorrect edge flow");
22481ad6265SDimitry Andric       uint64_t EdgeCapacity = uint64_t(Edge.Capacity - Edge.Flow);
22581ad6265SDimitry Andric       PathCapacity = std::min(PathCapacity, EdgeCapacity);
22681ad6265SDimitry Andric 
22781ad6265SDimitry Andric       Now = Pred;
22881ad6265SDimitry Andric     }
22981ad6265SDimitry Andric     return PathCapacity;
23081ad6265SDimitry Andric   }
23181ad6265SDimitry Andric 
2324824e7fdSDimitry Andric   /// Check for existence of an augmenting path with a positive capacity.
2334824e7fdSDimitry Andric   bool findAugmentingPath() {
2344824e7fdSDimitry Andric     // Initialize data structures
2354824e7fdSDimitry Andric     for (auto &Node : Nodes) {
2364824e7fdSDimitry Andric       Node.Distance = INF;
2374824e7fdSDimitry Andric       Node.ParentNode = uint64_t(-1);
2384824e7fdSDimitry Andric       Node.ParentEdgeIndex = uint64_t(-1);
2394824e7fdSDimitry Andric       Node.Taken = false;
2404824e7fdSDimitry Andric     }
2414824e7fdSDimitry Andric 
2424824e7fdSDimitry Andric     std::queue<uint64_t> Queue;
2434824e7fdSDimitry Andric     Queue.push(Source);
2444824e7fdSDimitry Andric     Nodes[Source].Distance = 0;
2454824e7fdSDimitry Andric     Nodes[Source].Taken = true;
2464824e7fdSDimitry Andric     while (!Queue.empty()) {
2474824e7fdSDimitry Andric       uint64_t Src = Queue.front();
2484824e7fdSDimitry Andric       Queue.pop();
2494824e7fdSDimitry Andric       Nodes[Src].Taken = false;
2504824e7fdSDimitry Andric       // Although the residual network contains edges with negative costs
2514824e7fdSDimitry Andric       // (in particular, backward edges), it can be shown that there are no
2524824e7fdSDimitry Andric       // negative-weight cycles and the following two invariants are maintained:
2534824e7fdSDimitry Andric       // (i) Dist[Source, V] >= 0 and (ii) Dist[V, Target] >= 0 for all nodes V,
2544824e7fdSDimitry Andric       // where Dist is the length of the shortest path between two nodes. This
2554824e7fdSDimitry Andric       // allows to prune the search-space of the path-finding algorithm using
2564824e7fdSDimitry Andric       // the following early-stop criteria:
2574824e7fdSDimitry Andric       // -- If we find a path with zero-distance from Source to Target, stop the
2584824e7fdSDimitry Andric       //    search, as the path is the shortest since Dist[Source, Target] >= 0;
2594824e7fdSDimitry Andric       // -- If we have Dist[Source, V] > Dist[Source, Target], then do not
2604824e7fdSDimitry Andric       //    process node V, as it is guaranteed _not_ to be on a shortest path
2614824e7fdSDimitry Andric       //    from Source to Target; it follows from inequalities
2624824e7fdSDimitry Andric       //    Dist[Source, Target] >= Dist[Source, V] + Dist[V, Target]
2634824e7fdSDimitry Andric       //                         >= Dist[Source, V]
264bdd1243dSDimitry Andric       if (!Params.EvenFlowDistribution && Nodes[Target].Distance == 0)
2654824e7fdSDimitry Andric         break;
2664824e7fdSDimitry Andric       if (Nodes[Src].Distance > Nodes[Target].Distance)
2674824e7fdSDimitry Andric         continue;
2684824e7fdSDimitry Andric 
2694824e7fdSDimitry Andric       // Process adjacent edges
2704824e7fdSDimitry Andric       for (uint64_t EdgeIdx = 0; EdgeIdx < Edges[Src].size(); EdgeIdx++) {
2714824e7fdSDimitry Andric         auto &Edge = Edges[Src][EdgeIdx];
2724824e7fdSDimitry Andric         if (Edge.Flow < Edge.Capacity) {
2734824e7fdSDimitry Andric           uint64_t Dst = Edge.Dst;
2744824e7fdSDimitry Andric           int64_t NewDistance = Nodes[Src].Distance + Edge.Cost;
2754824e7fdSDimitry Andric           if (Nodes[Dst].Distance > NewDistance) {
2764824e7fdSDimitry Andric             // Update the distance and the parent node/edge
2774824e7fdSDimitry Andric             Nodes[Dst].Distance = NewDistance;
2784824e7fdSDimitry Andric             Nodes[Dst].ParentNode = Src;
2794824e7fdSDimitry Andric             Nodes[Dst].ParentEdgeIndex = EdgeIdx;
2804824e7fdSDimitry Andric             // Add the node to the queue, if it is not there yet
2814824e7fdSDimitry Andric             if (!Nodes[Dst].Taken) {
2824824e7fdSDimitry Andric               Queue.push(Dst);
2834824e7fdSDimitry Andric               Nodes[Dst].Taken = true;
2844824e7fdSDimitry Andric             }
2854824e7fdSDimitry Andric           }
2864824e7fdSDimitry Andric         }
2874824e7fdSDimitry Andric       }
2884824e7fdSDimitry Andric     }
2894824e7fdSDimitry Andric 
2904824e7fdSDimitry Andric     return Nodes[Target].Distance != INF;
2914824e7fdSDimitry Andric   }
2924824e7fdSDimitry Andric 
2934824e7fdSDimitry Andric   /// Update the current flow along the augmenting path.
29481ad6265SDimitry Andric   void augmentFlowAlongPath(uint64_t PathCapacity) {
2950eae32dcSDimitry Andric     assert(PathCapacity > 0 && "found an incorrect augmenting path");
29681ad6265SDimitry Andric     uint64_t Now = Target;
2974824e7fdSDimitry Andric     while (Now != Source) {
2984824e7fdSDimitry Andric       uint64_t Pred = Nodes[Now].ParentNode;
2994824e7fdSDimitry Andric       auto &Edge = Edges[Pred][Nodes[Now].ParentEdgeIndex];
3004824e7fdSDimitry Andric       auto &RevEdge = Edges[Now][Edge.RevEdgeIndex];
3014824e7fdSDimitry Andric 
3024824e7fdSDimitry Andric       Edge.Flow += PathCapacity;
3034824e7fdSDimitry Andric       RevEdge.Flow -= PathCapacity;
3044824e7fdSDimitry Andric 
3054824e7fdSDimitry Andric       Now = Pred;
3064824e7fdSDimitry Andric     }
3074824e7fdSDimitry Andric   }
3084824e7fdSDimitry Andric 
30981ad6265SDimitry Andric   /// Find an Augmenting DAG order using a modified version of DFS in which we
31081ad6265SDimitry Andric   /// can visit a node multiple times. In the DFS search, when scanning each
31181ad6265SDimitry Andric   /// edge out of a node, continue search at Edge.Dst endpoint if it has not
31281ad6265SDimitry Andric   /// been discovered yet and its NumCalls < MaxDfsCalls. The algorithm
31381ad6265SDimitry Andric   /// runs in O(MaxDfsCalls * |Edges| + |Nodes|) time.
31481ad6265SDimitry Andric   /// It returns an Augmenting Order (Taken nodes in decreasing Finish time)
31581ad6265SDimitry Andric   /// that starts with Source and ends with Target.
31681ad6265SDimitry Andric   std::vector<uint64_t> findAugmentingDAG() {
31781ad6265SDimitry Andric     // We use a stack based implemenation of DFS to avoid recursion.
31881ad6265SDimitry Andric     // Defining DFS data structures:
31981ad6265SDimitry Andric     // A pair (NodeIdx, EdgeIdx) at the top of the Stack denotes that
32081ad6265SDimitry Andric     //  - we are currently visiting Nodes[NodeIdx] and
32181ad6265SDimitry Andric     //  - the next edge to scan is Edges[NodeIdx][EdgeIdx]
32281ad6265SDimitry Andric     typedef std::pair<uint64_t, uint64_t> StackItemType;
32381ad6265SDimitry Andric     std::stack<StackItemType> Stack;
32481ad6265SDimitry Andric     std::vector<uint64_t> AugmentingOrder;
32581ad6265SDimitry Andric 
32681ad6265SDimitry Andric     // Phase 0: Initialize Node attributes and Time for DFS run
32781ad6265SDimitry Andric     for (auto &Node : Nodes) {
32881ad6265SDimitry Andric       Node.Discovery = 0;
32981ad6265SDimitry Andric       Node.Finish = 0;
33081ad6265SDimitry Andric       Node.NumCalls = 0;
33181ad6265SDimitry Andric       Node.Taken = false;
33281ad6265SDimitry Andric     }
33381ad6265SDimitry Andric     uint64_t Time = 0;
33481ad6265SDimitry Andric     // Mark Target as Taken
33581ad6265SDimitry Andric     // Taken attribute will be propagated backwards from Target towards Source
33681ad6265SDimitry Andric     Nodes[Target].Taken = true;
33781ad6265SDimitry Andric 
33881ad6265SDimitry Andric     // Phase 1: Start DFS traversal from Source
33981ad6265SDimitry Andric     Stack.emplace(Source, 0);
34081ad6265SDimitry Andric     Nodes[Source].Discovery = ++Time;
34181ad6265SDimitry Andric     while (!Stack.empty()) {
34281ad6265SDimitry Andric       auto NodeIdx = Stack.top().first;
34381ad6265SDimitry Andric       auto EdgeIdx = Stack.top().second;
34481ad6265SDimitry Andric 
34581ad6265SDimitry Andric       // If we haven't scanned all edges out of NodeIdx, continue scanning
34681ad6265SDimitry Andric       if (EdgeIdx < Edges[NodeIdx].size()) {
34781ad6265SDimitry Andric         auto &Edge = Edges[NodeIdx][EdgeIdx];
34881ad6265SDimitry Andric         auto &Dst = Nodes[Edge.Dst];
34981ad6265SDimitry Andric         Stack.top().second++;
35081ad6265SDimitry Andric 
35181ad6265SDimitry Andric         if (Edge.OnShortestPath) {
35281ad6265SDimitry Andric           // If we haven't seen Edge.Dst so far, continue DFS search there
353bdd1243dSDimitry Andric           if (Dst.Discovery == 0 && Dst.NumCalls < MaxDfsCalls) {
35481ad6265SDimitry Andric             Dst.Discovery = ++Time;
35581ad6265SDimitry Andric             Stack.emplace(Edge.Dst, 0);
35681ad6265SDimitry Andric             Dst.NumCalls++;
35781ad6265SDimitry Andric           } else if (Dst.Taken && Dst.Finish != 0) {
35881ad6265SDimitry Andric             // Else, if Edge.Dst already have a path to Target, so that NodeIdx
35981ad6265SDimitry Andric             Nodes[NodeIdx].Taken = true;
36081ad6265SDimitry Andric           }
36181ad6265SDimitry Andric         }
36281ad6265SDimitry Andric       } else {
36381ad6265SDimitry Andric         // If we are done scanning all edge out of NodeIdx
36481ad6265SDimitry Andric         Stack.pop();
36581ad6265SDimitry Andric         // If we haven't found a path from NodeIdx to Target, forget about it
36681ad6265SDimitry Andric         if (!Nodes[NodeIdx].Taken) {
36781ad6265SDimitry Andric           Nodes[NodeIdx].Discovery = 0;
36881ad6265SDimitry Andric         } else {
36981ad6265SDimitry Andric           // If we have found a path from NodeIdx to Target, then finish NodeIdx
37081ad6265SDimitry Andric           // and propagate Taken flag to DFS parent unless at the Source
37181ad6265SDimitry Andric           Nodes[NodeIdx].Finish = ++Time;
37281ad6265SDimitry Andric           // NodeIdx == Source if and only if the stack is empty
37381ad6265SDimitry Andric           if (NodeIdx != Source) {
37481ad6265SDimitry Andric             assert(!Stack.empty() && "empty stack while running dfs");
37581ad6265SDimitry Andric             Nodes[Stack.top().first].Taken = true;
37681ad6265SDimitry Andric           }
37781ad6265SDimitry Andric           AugmentingOrder.push_back(NodeIdx);
37881ad6265SDimitry Andric         }
37981ad6265SDimitry Andric       }
38081ad6265SDimitry Andric     }
38181ad6265SDimitry Andric     // Nodes are collected decreasing Finish time, so the order is reversed
38281ad6265SDimitry Andric     std::reverse(AugmentingOrder.begin(), AugmentingOrder.end());
38381ad6265SDimitry Andric 
38481ad6265SDimitry Andric     // Phase 2: Extract all forward (DAG) edges and fill in AugmentingEdges
38581ad6265SDimitry Andric     for (size_t Src : AugmentingOrder) {
38681ad6265SDimitry Andric       AugmentingEdges[Src].clear();
38781ad6265SDimitry Andric       for (auto &Edge : Edges[Src]) {
38881ad6265SDimitry Andric         uint64_t Dst = Edge.Dst;
38981ad6265SDimitry Andric         if (Edge.OnShortestPath && Nodes[Src].Taken && Nodes[Dst].Taken &&
39081ad6265SDimitry Andric             Nodes[Dst].Finish < Nodes[Src].Finish) {
39181ad6265SDimitry Andric           AugmentingEdges[Src].push_back(&Edge);
39281ad6265SDimitry Andric         }
39381ad6265SDimitry Andric       }
39481ad6265SDimitry Andric       assert((Src == Target || !AugmentingEdges[Src].empty()) &&
39581ad6265SDimitry Andric              "incorrectly constructed augmenting edges");
39681ad6265SDimitry Andric     }
39781ad6265SDimitry Andric 
39881ad6265SDimitry Andric     return AugmentingOrder;
39981ad6265SDimitry Andric   }
40081ad6265SDimitry Andric 
40181ad6265SDimitry Andric   /// Update the current flow along the given (acyclic) subgraph specified by
40281ad6265SDimitry Andric   /// the vertex order, AugmentingOrder. The objective is to send as much flow
40381ad6265SDimitry Andric   /// as possible while evenly distributing flow among successors of each node.
40481ad6265SDimitry Andric   /// After the update at least one edge is saturated.
40581ad6265SDimitry Andric   bool augmentFlowAlongDAG(const std::vector<uint64_t> &AugmentingOrder) {
40681ad6265SDimitry Andric     // Phase 0: Initialization
40781ad6265SDimitry Andric     for (uint64_t Src : AugmentingOrder) {
40881ad6265SDimitry Andric       Nodes[Src].FracFlow = 0;
40981ad6265SDimitry Andric       Nodes[Src].IntFlow = 0;
41081ad6265SDimitry Andric       for (auto &Edge : AugmentingEdges[Src]) {
41181ad6265SDimitry Andric         Edge->AugmentedFlow = 0;
41281ad6265SDimitry Andric       }
41381ad6265SDimitry Andric     }
41481ad6265SDimitry Andric 
41581ad6265SDimitry Andric     // Phase 1: Send a unit of fractional flow along the DAG
41681ad6265SDimitry Andric     uint64_t MaxFlowAmount = INF;
41781ad6265SDimitry Andric     Nodes[Source].FracFlow = 1.0;
41881ad6265SDimitry Andric     for (uint64_t Src : AugmentingOrder) {
41981ad6265SDimitry Andric       assert((Src == Target || Nodes[Src].FracFlow > 0.0) &&
42081ad6265SDimitry Andric              "incorrectly computed fractional flow");
42181ad6265SDimitry Andric       // Distribute flow evenly among successors of Src
42281ad6265SDimitry Andric       uint64_t Degree = AugmentingEdges[Src].size();
42381ad6265SDimitry Andric       for (auto &Edge : AugmentingEdges[Src]) {
42481ad6265SDimitry Andric         double EdgeFlow = Nodes[Src].FracFlow / Degree;
42581ad6265SDimitry Andric         Nodes[Edge->Dst].FracFlow += EdgeFlow;
42681ad6265SDimitry Andric         if (Edge->Capacity == INF)
42781ad6265SDimitry Andric           continue;
42881ad6265SDimitry Andric         uint64_t MaxIntFlow = double(Edge->Capacity - Edge->Flow) / EdgeFlow;
42981ad6265SDimitry Andric         MaxFlowAmount = std::min(MaxFlowAmount, MaxIntFlow);
43081ad6265SDimitry Andric       }
43181ad6265SDimitry Andric     }
43281ad6265SDimitry Andric     // Stop early if we cannot send any (integral) flow from Source to Target
43381ad6265SDimitry Andric     if (MaxFlowAmount == 0)
43481ad6265SDimitry Andric       return false;
43581ad6265SDimitry Andric 
43681ad6265SDimitry Andric     // Phase 2: Send an integral flow of MaxFlowAmount
43781ad6265SDimitry Andric     Nodes[Source].IntFlow = MaxFlowAmount;
43881ad6265SDimitry Andric     for (uint64_t Src : AugmentingOrder) {
43981ad6265SDimitry Andric       if (Src == Target)
44081ad6265SDimitry Andric         break;
44181ad6265SDimitry Andric       // Distribute flow evenly among successors of Src, rounding up to make
44281ad6265SDimitry Andric       // sure all flow is sent
44381ad6265SDimitry Andric       uint64_t Degree = AugmentingEdges[Src].size();
44481ad6265SDimitry Andric       // We are guaranteeed that Node[Src].IntFlow <= SuccFlow * Degree
44581ad6265SDimitry Andric       uint64_t SuccFlow = (Nodes[Src].IntFlow + Degree - 1) / Degree;
44681ad6265SDimitry Andric       for (auto &Edge : AugmentingEdges[Src]) {
44781ad6265SDimitry Andric         uint64_t Dst = Edge->Dst;
44881ad6265SDimitry Andric         uint64_t EdgeFlow = std::min(Nodes[Src].IntFlow, SuccFlow);
44981ad6265SDimitry Andric         EdgeFlow = std::min(EdgeFlow, uint64_t(Edge->Capacity - Edge->Flow));
45081ad6265SDimitry Andric         Nodes[Dst].IntFlow += EdgeFlow;
45181ad6265SDimitry Andric         Nodes[Src].IntFlow -= EdgeFlow;
45281ad6265SDimitry Andric         Edge->AugmentedFlow += EdgeFlow;
45381ad6265SDimitry Andric       }
45481ad6265SDimitry Andric     }
45581ad6265SDimitry Andric     assert(Nodes[Target].IntFlow <= MaxFlowAmount);
45681ad6265SDimitry Andric     Nodes[Target].IntFlow = 0;
45781ad6265SDimitry Andric 
45881ad6265SDimitry Andric     // Phase 3: Send excess flow back traversing the nodes backwards.
45981ad6265SDimitry Andric     // Because of rounding, not all flow can be sent along the edges of Src.
46081ad6265SDimitry Andric     // Hence, sending the remaining flow back to maintain flow conservation
46181ad6265SDimitry Andric     for (size_t Idx = AugmentingOrder.size() - 1; Idx > 0; Idx--) {
46281ad6265SDimitry Andric       uint64_t Src = AugmentingOrder[Idx - 1];
46381ad6265SDimitry Andric       // Try to send excess flow back along each edge.
46481ad6265SDimitry Andric       // Make sure we only send back flow we just augmented (AugmentedFlow).
46581ad6265SDimitry Andric       for (auto &Edge : AugmentingEdges[Src]) {
46681ad6265SDimitry Andric         uint64_t Dst = Edge->Dst;
46781ad6265SDimitry Andric         if (Nodes[Dst].IntFlow == 0)
46881ad6265SDimitry Andric           continue;
46981ad6265SDimitry Andric         uint64_t EdgeFlow = std::min(Nodes[Dst].IntFlow, Edge->AugmentedFlow);
47081ad6265SDimitry Andric         Nodes[Dst].IntFlow -= EdgeFlow;
47181ad6265SDimitry Andric         Nodes[Src].IntFlow += EdgeFlow;
47281ad6265SDimitry Andric         Edge->AugmentedFlow -= EdgeFlow;
47381ad6265SDimitry Andric       }
47481ad6265SDimitry Andric     }
47581ad6265SDimitry Andric 
47681ad6265SDimitry Andric     // Phase 4: Update flow values along all edges
47781ad6265SDimitry Andric     bool HasSaturatedEdges = false;
47881ad6265SDimitry Andric     for (uint64_t Src : AugmentingOrder) {
47981ad6265SDimitry Andric       // Verify that we have sent all the excess flow from the node
48081ad6265SDimitry Andric       assert(Src == Source || Nodes[Src].IntFlow == 0);
48181ad6265SDimitry Andric       for (auto &Edge : AugmentingEdges[Src]) {
48281ad6265SDimitry Andric         assert(uint64_t(Edge->Capacity - Edge->Flow) >= Edge->AugmentedFlow);
48381ad6265SDimitry Andric         // Update flow values along the edge and its reverse copy
48481ad6265SDimitry Andric         auto &RevEdge = Edges[Edge->Dst][Edge->RevEdgeIndex];
48581ad6265SDimitry Andric         Edge->Flow += Edge->AugmentedFlow;
48681ad6265SDimitry Andric         RevEdge.Flow -= Edge->AugmentedFlow;
48781ad6265SDimitry Andric         if (Edge->Capacity == Edge->Flow && Edge->AugmentedFlow > 0)
48881ad6265SDimitry Andric           HasSaturatedEdges = true;
48981ad6265SDimitry Andric       }
49081ad6265SDimitry Andric     }
49181ad6265SDimitry Andric 
49281ad6265SDimitry Andric     // The augmentation is successful iff at least one edge becomes saturated
49381ad6265SDimitry Andric     return HasSaturatedEdges;
49481ad6265SDimitry Andric   }
49581ad6265SDimitry Andric 
49681ad6265SDimitry Andric   /// Identify candidate (shortest) edges for augmentation.
49781ad6265SDimitry Andric   void identifyShortestEdges(uint64_t PathCapacity) {
49881ad6265SDimitry Andric     assert(PathCapacity > 0 && "found an incorrect augmenting DAG");
49981ad6265SDimitry Andric     // To make sure the augmentation DAG contains only edges with large residual
50081ad6265SDimitry Andric     // capacity, we prune all edges whose capacity is below a fraction of
50181ad6265SDimitry Andric     // the capacity of the augmented path.
50281ad6265SDimitry Andric     // (All edges of the path itself are always in the DAG)
50381ad6265SDimitry Andric     uint64_t MinCapacity = std::max(PathCapacity / 2, uint64_t(1));
50481ad6265SDimitry Andric 
50581ad6265SDimitry Andric     // Decide which edges are on a shortest path from Source to Target
50681ad6265SDimitry Andric     for (size_t Src = 0; Src < Nodes.size(); Src++) {
50781ad6265SDimitry Andric       // An edge cannot be augmenting if the endpoint has large distance
50881ad6265SDimitry Andric       if (Nodes[Src].Distance > Nodes[Target].Distance)
50981ad6265SDimitry Andric         continue;
51081ad6265SDimitry Andric 
51181ad6265SDimitry Andric       for (auto &Edge : Edges[Src]) {
51281ad6265SDimitry Andric         uint64_t Dst = Edge.Dst;
51381ad6265SDimitry Andric         Edge.OnShortestPath =
51481ad6265SDimitry Andric             Src != Target && Dst != Source &&
51581ad6265SDimitry Andric             Nodes[Dst].Distance <= Nodes[Target].Distance &&
51681ad6265SDimitry Andric             Nodes[Dst].Distance == Nodes[Src].Distance + Edge.Cost &&
51781ad6265SDimitry Andric             Edge.Capacity > Edge.Flow &&
51881ad6265SDimitry Andric             uint64_t(Edge.Capacity - Edge.Flow) >= MinCapacity;
51981ad6265SDimitry Andric       }
52081ad6265SDimitry Andric     }
52181ad6265SDimitry Andric   }
52281ad6265SDimitry Andric 
523bdd1243dSDimitry Andric   /// Maximum number of DFS iterations for DAG finding.
524bdd1243dSDimitry Andric   static constexpr uint64_t MaxDfsCalls = 10;
525bdd1243dSDimitry Andric 
52604eeddc0SDimitry Andric   /// A node in a flow network.
5274824e7fdSDimitry Andric   struct Node {
5284824e7fdSDimitry Andric     /// The cost of the cheapest path from the source to the current node.
5294824e7fdSDimitry Andric     int64_t Distance;
5304824e7fdSDimitry Andric     /// The node preceding the current one in the path.
5314824e7fdSDimitry Andric     uint64_t ParentNode;
5324824e7fdSDimitry Andric     /// The index of the edge between ParentNode and the current node.
5334824e7fdSDimitry Andric     uint64_t ParentEdgeIndex;
5344824e7fdSDimitry Andric     /// An indicator of whether the current node is in a queue.
5354824e7fdSDimitry Andric     bool Taken;
53681ad6265SDimitry Andric 
53781ad6265SDimitry Andric     /// Data fields utilized in DAG-augmentation:
53881ad6265SDimitry Andric     /// Fractional flow.
53981ad6265SDimitry Andric     double FracFlow;
54081ad6265SDimitry Andric     /// Integral flow.
54181ad6265SDimitry Andric     uint64_t IntFlow;
54281ad6265SDimitry Andric     /// Discovery time.
54381ad6265SDimitry Andric     uint64_t Discovery;
54481ad6265SDimitry Andric     /// Finish time.
54581ad6265SDimitry Andric     uint64_t Finish;
54681ad6265SDimitry Andric     /// NumCalls.
54781ad6265SDimitry Andric     uint64_t NumCalls;
5484824e7fdSDimitry Andric   };
54981ad6265SDimitry Andric 
5504824e7fdSDimitry Andric   /// An edge in a flow network.
5514824e7fdSDimitry Andric   struct Edge {
5524824e7fdSDimitry Andric     /// The cost of the edge.
5534824e7fdSDimitry Andric     int64_t Cost;
5544824e7fdSDimitry Andric     /// The capacity of the edge.
5554824e7fdSDimitry Andric     int64_t Capacity;
5564824e7fdSDimitry Andric     /// The current flow on the edge.
5574824e7fdSDimitry Andric     int64_t Flow;
5584824e7fdSDimitry Andric     /// The destination node of the edge.
5594824e7fdSDimitry Andric     uint64_t Dst;
5604824e7fdSDimitry Andric     /// The index of the reverse edge between Dst and the current node.
5614824e7fdSDimitry Andric     uint64_t RevEdgeIndex;
56281ad6265SDimitry Andric 
56381ad6265SDimitry Andric     /// Data fields utilized in DAG-augmentation:
56481ad6265SDimitry Andric     /// Whether the edge is currently on a shortest path from Source to Target.
56581ad6265SDimitry Andric     bool OnShortestPath;
56681ad6265SDimitry Andric     /// Extra flow along the edge.
56781ad6265SDimitry Andric     uint64_t AugmentedFlow;
5684824e7fdSDimitry Andric   };
5694824e7fdSDimitry Andric 
5704824e7fdSDimitry Andric   /// The set of network nodes.
5714824e7fdSDimitry Andric   std::vector<Node> Nodes;
5724824e7fdSDimitry Andric   /// The set of network edges.
5734824e7fdSDimitry Andric   std::vector<std::vector<Edge>> Edges;
5744824e7fdSDimitry Andric   /// Source node of the flow.
5754824e7fdSDimitry Andric   uint64_t Source;
5764824e7fdSDimitry Andric   /// Target (sink) node of the flow.
5774824e7fdSDimitry Andric   uint64_t Target;
57881ad6265SDimitry Andric   /// Augmenting edges.
57981ad6265SDimitry Andric   std::vector<std::vector<Edge *>> AugmentingEdges;
580bdd1243dSDimitry Andric   /// Params for flow computation.
581bdd1243dSDimitry Andric   const ProfiParams &Params;
5824824e7fdSDimitry Andric };
5834824e7fdSDimitry Andric 
584bdd1243dSDimitry Andric /// A post-processing adjustment of the control flow. It applies two steps by
5850eae32dcSDimitry Andric /// rerouting some flow and making it more realistic:
5860eae32dcSDimitry Andric ///
5870eae32dcSDimitry Andric /// - First, it removes all isolated components ("islands") with a positive flow
5880eae32dcSDimitry Andric ///   that are unreachable from the entry block. For every such component, we
5890eae32dcSDimitry Andric ///   find the shortest from the entry to an exit passing through the component,
5900eae32dcSDimitry Andric ///   and increase the flow by one unit along the path.
5910eae32dcSDimitry Andric ///
5920eae32dcSDimitry Andric /// - Second, it identifies all "unknown subgraphs" consisting of basic blocks
5930eae32dcSDimitry Andric ///   with no sampled counts. Then it rebalnces the flow that goes through such
5940eae32dcSDimitry Andric ///   a subgraph so that each branch is taken with probability 50%.
5950eae32dcSDimitry Andric ///   An unknown subgraph is such that for every two nodes u and v:
5960eae32dcSDimitry Andric ///     - u dominates v and u is not unknown;
5970eae32dcSDimitry Andric ///     - v post-dominates u; and
5980eae32dcSDimitry Andric ///     - all inner-nodes of all (u,v)-paths are unknown.
5990eae32dcSDimitry Andric ///
6000eae32dcSDimitry Andric class FlowAdjuster {
6010eae32dcSDimitry Andric public:
602bdd1243dSDimitry Andric   FlowAdjuster(const ProfiParams &Params, FlowFunction &Func)
603bdd1243dSDimitry Andric       : Params(Params), Func(Func) {}
604bdd1243dSDimitry Andric 
605bdd1243dSDimitry Andric   /// Apply the post-processing.
606bdd1243dSDimitry Andric   void run() {
607bdd1243dSDimitry Andric     if (Params.JoinIslands) {
608bdd1243dSDimitry Andric       // Adjust the flow to get rid of isolated components
609bdd1243dSDimitry Andric       joinIsolatedComponents();
6100eae32dcSDimitry Andric     }
6110eae32dcSDimitry Andric 
612bdd1243dSDimitry Andric     if (Params.RebalanceUnknown) {
613bdd1243dSDimitry Andric       // Rebalance the flow inside unknown subgraphs
6140eae32dcSDimitry Andric       rebalanceUnknownSubgraphs();
6150eae32dcSDimitry Andric     }
616bdd1243dSDimitry Andric   }
6170eae32dcSDimitry Andric 
6180eae32dcSDimitry Andric private:
6190eae32dcSDimitry Andric   void joinIsolatedComponents() {
6200eae32dcSDimitry Andric     // Find blocks that are reachable from the source
62104eeddc0SDimitry Andric     auto Visited = BitVector(NumBlocks(), false);
6220eae32dcSDimitry Andric     findReachable(Func.Entry, Visited);
6230eae32dcSDimitry Andric 
6240eae32dcSDimitry Andric     // Iterate over all non-reachable blocks and adjust their weights
6250eae32dcSDimitry Andric     for (uint64_t I = 0; I < NumBlocks(); I++) {
6260eae32dcSDimitry Andric       auto &Block = Func.Blocks[I];
6270eae32dcSDimitry Andric       if (Block.Flow > 0 && !Visited[I]) {
6280eae32dcSDimitry Andric         // Find a path from the entry to an exit passing through the block I
6290eae32dcSDimitry Andric         auto Path = findShortestPath(I);
6300eae32dcSDimitry Andric         // Increase the flow along the path
6310eae32dcSDimitry Andric         assert(Path.size() > 0 && Path[0]->Source == Func.Entry &&
6320eae32dcSDimitry Andric                "incorrectly computed path adjusting control flow");
6330eae32dcSDimitry Andric         Func.Blocks[Func.Entry].Flow += 1;
6340eae32dcSDimitry Andric         for (auto &Jump : Path) {
6350eae32dcSDimitry Andric           Jump->Flow += 1;
6360eae32dcSDimitry Andric           Func.Blocks[Jump->Target].Flow += 1;
6370eae32dcSDimitry Andric           // Update reachability
6380eae32dcSDimitry Andric           findReachable(Jump->Target, Visited);
6390eae32dcSDimitry Andric         }
6400eae32dcSDimitry Andric       }
6410eae32dcSDimitry Andric     }
6420eae32dcSDimitry Andric   }
6430eae32dcSDimitry Andric 
6440eae32dcSDimitry Andric   /// Run BFS from a given block along the jumps with a positive flow and mark
6450eae32dcSDimitry Andric   /// all reachable blocks.
64604eeddc0SDimitry Andric   void findReachable(uint64_t Src, BitVector &Visited) {
6470eae32dcSDimitry Andric     if (Visited[Src])
6480eae32dcSDimitry Andric       return;
6490eae32dcSDimitry Andric     std::queue<uint64_t> Queue;
6500eae32dcSDimitry Andric     Queue.push(Src);
6510eae32dcSDimitry Andric     Visited[Src] = true;
6520eae32dcSDimitry Andric     while (!Queue.empty()) {
6530eae32dcSDimitry Andric       Src = Queue.front();
6540eae32dcSDimitry Andric       Queue.pop();
655bdd1243dSDimitry Andric       for (auto *Jump : Func.Blocks[Src].SuccJumps) {
6560eae32dcSDimitry Andric         uint64_t Dst = Jump->Target;
6570eae32dcSDimitry Andric         if (Jump->Flow > 0 && !Visited[Dst]) {
6580eae32dcSDimitry Andric           Queue.push(Dst);
6590eae32dcSDimitry Andric           Visited[Dst] = true;
6600eae32dcSDimitry Andric         }
6610eae32dcSDimitry Andric       }
6620eae32dcSDimitry Andric     }
6630eae32dcSDimitry Andric   }
6640eae32dcSDimitry Andric 
6650eae32dcSDimitry Andric   /// Find the shortest path from the entry block to an exit block passing
6660eae32dcSDimitry Andric   /// through a given block.
6670eae32dcSDimitry Andric   std::vector<FlowJump *> findShortestPath(uint64_t BlockIdx) {
6680eae32dcSDimitry Andric     // A path from the entry block to BlockIdx
6690eae32dcSDimitry Andric     auto ForwardPath = findShortestPath(Func.Entry, BlockIdx);
6700eae32dcSDimitry Andric     // A path from BlockIdx to an exit block
6710eae32dcSDimitry Andric     auto BackwardPath = findShortestPath(BlockIdx, AnyExitBlock);
6720eae32dcSDimitry Andric 
6730eae32dcSDimitry Andric     // Concatenate the two paths
6740eae32dcSDimitry Andric     std::vector<FlowJump *> Result;
6750eae32dcSDimitry Andric     Result.insert(Result.end(), ForwardPath.begin(), ForwardPath.end());
6760eae32dcSDimitry Andric     Result.insert(Result.end(), BackwardPath.begin(), BackwardPath.end());
6770eae32dcSDimitry Andric     return Result;
6780eae32dcSDimitry Andric   }
6790eae32dcSDimitry Andric 
6800eae32dcSDimitry Andric   /// Apply the Dijkstra algorithm to find the shortest path from a given
6810eae32dcSDimitry Andric   /// Source to a given Target block.
6820eae32dcSDimitry Andric   /// If Target == -1, then the path ends at an exit block.
6830eae32dcSDimitry Andric   std::vector<FlowJump *> findShortestPath(uint64_t Source, uint64_t Target) {
6840eae32dcSDimitry Andric     // Quit early, if possible
6850eae32dcSDimitry Andric     if (Source == Target)
6860eae32dcSDimitry Andric       return std::vector<FlowJump *>();
6870eae32dcSDimitry Andric     if (Func.Blocks[Source].isExit() && Target == AnyExitBlock)
6880eae32dcSDimitry Andric       return std::vector<FlowJump *>();
6890eae32dcSDimitry Andric 
6900eae32dcSDimitry Andric     // Initialize data structures
6910eae32dcSDimitry Andric     auto Distance = std::vector<int64_t>(NumBlocks(), INF);
6920eae32dcSDimitry Andric     auto Parent = std::vector<FlowJump *>(NumBlocks(), nullptr);
6930eae32dcSDimitry Andric     Distance[Source] = 0;
6940eae32dcSDimitry Andric     std::set<std::pair<uint64_t, uint64_t>> Queue;
6950eae32dcSDimitry Andric     Queue.insert(std::make_pair(Distance[Source], Source));
6960eae32dcSDimitry Andric 
6970eae32dcSDimitry Andric     // Run the Dijkstra algorithm
6980eae32dcSDimitry Andric     while (!Queue.empty()) {
6990eae32dcSDimitry Andric       uint64_t Src = Queue.begin()->second;
7000eae32dcSDimitry Andric       Queue.erase(Queue.begin());
7010eae32dcSDimitry Andric       // If we found a solution, quit early
7020eae32dcSDimitry Andric       if (Src == Target ||
7030eae32dcSDimitry Andric           (Func.Blocks[Src].isExit() && Target == AnyExitBlock))
7040eae32dcSDimitry Andric         break;
7050eae32dcSDimitry Andric 
706bdd1243dSDimitry Andric       for (auto *Jump : Func.Blocks[Src].SuccJumps) {
7070eae32dcSDimitry Andric         uint64_t Dst = Jump->Target;
7080eae32dcSDimitry Andric         int64_t JumpDist = jumpDistance(Jump);
7090eae32dcSDimitry Andric         if (Distance[Dst] > Distance[Src] + JumpDist) {
7100eae32dcSDimitry Andric           Queue.erase(std::make_pair(Distance[Dst], Dst));
7110eae32dcSDimitry Andric 
7120eae32dcSDimitry Andric           Distance[Dst] = Distance[Src] + JumpDist;
7130eae32dcSDimitry Andric           Parent[Dst] = Jump;
7140eae32dcSDimitry Andric 
7150eae32dcSDimitry Andric           Queue.insert(std::make_pair(Distance[Dst], Dst));
7160eae32dcSDimitry Andric         }
7170eae32dcSDimitry Andric       }
7180eae32dcSDimitry Andric     }
7190eae32dcSDimitry Andric     // If Target is not provided, find the closest exit block
7200eae32dcSDimitry Andric     if (Target == AnyExitBlock) {
7210eae32dcSDimitry Andric       for (uint64_t I = 0; I < NumBlocks(); I++) {
7220eae32dcSDimitry Andric         if (Func.Blocks[I].isExit() && Parent[I] != nullptr) {
7230eae32dcSDimitry Andric           if (Target == AnyExitBlock || Distance[Target] > Distance[I]) {
7240eae32dcSDimitry Andric             Target = I;
7250eae32dcSDimitry Andric           }
7260eae32dcSDimitry Andric         }
7270eae32dcSDimitry Andric       }
7280eae32dcSDimitry Andric     }
7290eae32dcSDimitry Andric     assert(Parent[Target] != nullptr && "a path does not exist");
7300eae32dcSDimitry Andric 
7310eae32dcSDimitry Andric     // Extract the constructed path
7320eae32dcSDimitry Andric     std::vector<FlowJump *> Result;
7330eae32dcSDimitry Andric     uint64_t Now = Target;
7340eae32dcSDimitry Andric     while (Now != Source) {
7350eae32dcSDimitry Andric       assert(Now == Parent[Now]->Target && "incorrect parent jump");
7360eae32dcSDimitry Andric       Result.push_back(Parent[Now]);
7370eae32dcSDimitry Andric       Now = Parent[Now]->Source;
7380eae32dcSDimitry Andric     }
7390eae32dcSDimitry Andric     // Reverse the path, since it is extracted from Target to Source
7400eae32dcSDimitry Andric     std::reverse(Result.begin(), Result.end());
7410eae32dcSDimitry Andric     return Result;
7420eae32dcSDimitry Andric   }
7430eae32dcSDimitry Andric 
7440eae32dcSDimitry Andric   /// A distance of a path for a given jump.
7450eae32dcSDimitry Andric   /// In order to incite the path to use blocks/jumps with large positive flow,
7460eae32dcSDimitry Andric   /// and avoid changing branch probability of outgoing edges drastically,
74781ad6265SDimitry Andric   /// set the jump distance so as:
74881ad6265SDimitry Andric   ///   - to minimize the number of unlikely jumps used and subject to that,
74981ad6265SDimitry Andric   ///   - to minimize the number of Flow == 0 jumps used and subject to that,
75081ad6265SDimitry Andric   ///   - minimizes total multiplicative Flow increase for the remaining edges.
75181ad6265SDimitry Andric   /// To capture this objective with integer distances, we round off fractional
75281ad6265SDimitry Andric   /// parts to a multiple of 1 / BaseDistance.
7530eae32dcSDimitry Andric   int64_t jumpDistance(FlowJump *Jump) const {
7540eae32dcSDimitry Andric     if (Jump->IsUnlikely)
755bdd1243dSDimitry Andric       return Params.CostUnlikely;
756bdd1243dSDimitry Andric     uint64_t BaseDistance =
757bdd1243dSDimitry Andric         std::max(FlowAdjuster::MinBaseDistance,
758bdd1243dSDimitry Andric                  std::min(Func.Blocks[Func.Entry].Flow,
759bdd1243dSDimitry Andric                           Params.CostUnlikely / (2 * (NumBlocks() + 1))));
7600eae32dcSDimitry Andric     if (Jump->Flow > 0)
76181ad6265SDimitry Andric       return BaseDistance + BaseDistance / Jump->Flow;
762bdd1243dSDimitry Andric     return 2 * BaseDistance * (NumBlocks() + 1);
7630eae32dcSDimitry Andric   };
7640eae32dcSDimitry Andric 
7650eae32dcSDimitry Andric   uint64_t NumBlocks() const { return Func.Blocks.size(); }
7660eae32dcSDimitry Andric 
76704eeddc0SDimitry Andric   /// Rebalance unknown subgraphs so that the flow is split evenly across the
76804eeddc0SDimitry Andric   /// outgoing branches of every block of the subgraph. The method iterates over
76904eeddc0SDimitry Andric   /// blocks with known weight and identifies unknown subgraphs rooted at the
77004eeddc0SDimitry Andric   /// blocks. Then it verifies if flow rebalancing is feasible and applies it.
7710eae32dcSDimitry Andric   void rebalanceUnknownSubgraphs() {
77204eeddc0SDimitry Andric     // Try to find unknown subgraphs from each block
773bdd1243dSDimitry Andric     for (const FlowBlock &SrcBlock : Func.Blocks) {
77404eeddc0SDimitry Andric       // Verify if rebalancing rooted at SrcBlock is feasible
775bdd1243dSDimitry Andric       if (!canRebalanceAtRoot(&SrcBlock))
7760eae32dcSDimitry Andric         continue;
7770eae32dcSDimitry Andric 
77804eeddc0SDimitry Andric       // Find an unknown subgraphs starting at SrcBlock. Along the way,
77904eeddc0SDimitry Andric       // fill in known destinations and intermediate unknown blocks.
78004eeddc0SDimitry Andric       std::vector<FlowBlock *> UnknownBlocks;
78104eeddc0SDimitry Andric       std::vector<FlowBlock *> KnownDstBlocks;
782bdd1243dSDimitry Andric       findUnknownSubgraph(&SrcBlock, KnownDstBlocks, UnknownBlocks);
78304eeddc0SDimitry Andric 
78404eeddc0SDimitry Andric       // Verify if rebalancing of the subgraph is feasible. If the search is
78504eeddc0SDimitry Andric       // successful, find the unique destination block (which can be null)
7860eae32dcSDimitry Andric       FlowBlock *DstBlock = nullptr;
787bdd1243dSDimitry Andric       if (!canRebalanceSubgraph(&SrcBlock, KnownDstBlocks, UnknownBlocks,
78804eeddc0SDimitry Andric                                 DstBlock))
7890eae32dcSDimitry Andric         continue;
79004eeddc0SDimitry Andric 
79104eeddc0SDimitry Andric       // We cannot rebalance subgraphs containing cycles among unknown blocks
792bdd1243dSDimitry Andric       if (!isAcyclicSubgraph(&SrcBlock, DstBlock, UnknownBlocks))
7930eae32dcSDimitry Andric         continue;
7940eae32dcSDimitry Andric 
7950eae32dcSDimitry Andric       // Rebalance the flow
796bdd1243dSDimitry Andric       rebalanceUnknownSubgraph(&SrcBlock, DstBlock, UnknownBlocks);
7970eae32dcSDimitry Andric     }
7980eae32dcSDimitry Andric   }
7990eae32dcSDimitry Andric 
80004eeddc0SDimitry Andric   /// Verify if rebalancing rooted at a given block is possible.
80104eeddc0SDimitry Andric   bool canRebalanceAtRoot(const FlowBlock *SrcBlock) {
80204eeddc0SDimitry Andric     // Do not attempt to find unknown subgraphs from an unknown or a
80304eeddc0SDimitry Andric     // zero-flow block
804bdd1243dSDimitry Andric     if (SrcBlock->HasUnknownWeight || SrcBlock->Flow == 0)
80504eeddc0SDimitry Andric       return false;
80604eeddc0SDimitry Andric 
80704eeddc0SDimitry Andric     // Do not attempt to process subgraphs from a block w/o unknown sucessors
80804eeddc0SDimitry Andric     bool HasUnknownSuccs = false;
809bdd1243dSDimitry Andric     for (auto *Jump : SrcBlock->SuccJumps) {
810bdd1243dSDimitry Andric       if (Func.Blocks[Jump->Target].HasUnknownWeight) {
81104eeddc0SDimitry Andric         HasUnknownSuccs = true;
81204eeddc0SDimitry Andric         break;
81304eeddc0SDimitry Andric       }
81404eeddc0SDimitry Andric     }
81504eeddc0SDimitry Andric     if (!HasUnknownSuccs)
81604eeddc0SDimitry Andric       return false;
81704eeddc0SDimitry Andric 
81804eeddc0SDimitry Andric     return true;
81904eeddc0SDimitry Andric   }
82004eeddc0SDimitry Andric 
82104eeddc0SDimitry Andric   /// Find an unknown subgraph starting at block SrcBlock. The method sets
82204eeddc0SDimitry Andric   /// identified destinations, KnownDstBlocks, and intermediate UnknownBlocks.
82304eeddc0SDimitry Andric   void findUnknownSubgraph(const FlowBlock *SrcBlock,
82404eeddc0SDimitry Andric                            std::vector<FlowBlock *> &KnownDstBlocks,
82504eeddc0SDimitry Andric                            std::vector<FlowBlock *> &UnknownBlocks) {
8260eae32dcSDimitry Andric     // Run BFS from SrcBlock and make sure all paths are going through unknown
82781ad6265SDimitry Andric     // blocks and end at a known DstBlock
82804eeddc0SDimitry Andric     auto Visited = BitVector(NumBlocks(), false);
8290eae32dcSDimitry Andric     std::queue<uint64_t> Queue;
8300eae32dcSDimitry Andric 
8310eae32dcSDimitry Andric     Queue.push(SrcBlock->Index);
8320eae32dcSDimitry Andric     Visited[SrcBlock->Index] = true;
8330eae32dcSDimitry Andric     while (!Queue.empty()) {
8340eae32dcSDimitry Andric       auto &Block = Func.Blocks[Queue.front()];
8350eae32dcSDimitry Andric       Queue.pop();
8360eae32dcSDimitry Andric       // Process blocks reachable from Block
837bdd1243dSDimitry Andric       for (auto *Jump : Block.SuccJumps) {
83804eeddc0SDimitry Andric         // If Jump can be ignored, skip it
83904eeddc0SDimitry Andric         if (ignoreJump(SrcBlock, nullptr, Jump))
84004eeddc0SDimitry Andric           continue;
84104eeddc0SDimitry Andric 
8420eae32dcSDimitry Andric         uint64_t Dst = Jump->Target;
84304eeddc0SDimitry Andric         // If Dst has been visited, skip Jump
8440eae32dcSDimitry Andric         if (Visited[Dst])
8450eae32dcSDimitry Andric           continue;
84604eeddc0SDimitry Andric         // Process block Dst
8470eae32dcSDimitry Andric         Visited[Dst] = true;
848bdd1243dSDimitry Andric         if (!Func.Blocks[Dst].HasUnknownWeight) {
84904eeddc0SDimitry Andric           KnownDstBlocks.push_back(&Func.Blocks[Dst]);
8500eae32dcSDimitry Andric         } else {
8510eae32dcSDimitry Andric           Queue.push(Dst);
85204eeddc0SDimitry Andric           UnknownBlocks.push_back(&Func.Blocks[Dst]);
85304eeddc0SDimitry Andric         }
8540eae32dcSDimitry Andric       }
8550eae32dcSDimitry Andric     }
8560eae32dcSDimitry Andric   }
8570eae32dcSDimitry Andric 
85804eeddc0SDimitry Andric   /// Verify if rebalancing of the subgraph is feasible. If the checks are
85904eeddc0SDimitry Andric   /// successful, set the unique destination block, DstBlock (can be null).
86004eeddc0SDimitry Andric   bool canRebalanceSubgraph(const FlowBlock *SrcBlock,
86104eeddc0SDimitry Andric                             const std::vector<FlowBlock *> &KnownDstBlocks,
86204eeddc0SDimitry Andric                             const std::vector<FlowBlock *> &UnknownBlocks,
86304eeddc0SDimitry Andric                             FlowBlock *&DstBlock) {
8640eae32dcSDimitry Andric     // If the list of unknown blocks is empty, we don't need rebalancing
86504eeddc0SDimitry Andric     if (UnknownBlocks.empty())
8660eae32dcSDimitry Andric       return false;
86704eeddc0SDimitry Andric 
86804eeddc0SDimitry Andric     // If there are multiple known sinks, we can't rebalance
86904eeddc0SDimitry Andric     if (KnownDstBlocks.size() > 1)
8700eae32dcSDimitry Andric       return false;
87104eeddc0SDimitry Andric     DstBlock = KnownDstBlocks.empty() ? nullptr : KnownDstBlocks.front();
87204eeddc0SDimitry Andric 
87304eeddc0SDimitry Andric     // Verify sinks of the subgraph
874bdd1243dSDimitry Andric     for (auto *Block : UnknownBlocks) {
87504eeddc0SDimitry Andric       if (Block->SuccJumps.empty()) {
87604eeddc0SDimitry Andric         // If there are multiple (known and unknown) sinks, we can't rebalance
87704eeddc0SDimitry Andric         if (DstBlock != nullptr)
87804eeddc0SDimitry Andric           return false;
87904eeddc0SDimitry Andric         continue;
88004eeddc0SDimitry Andric       }
88104eeddc0SDimitry Andric       size_t NumIgnoredJumps = 0;
882bdd1243dSDimitry Andric       for (auto *Jump : Block->SuccJumps) {
88304eeddc0SDimitry Andric         if (ignoreJump(SrcBlock, DstBlock, Jump))
88404eeddc0SDimitry Andric           NumIgnoredJumps++;
88504eeddc0SDimitry Andric       }
88604eeddc0SDimitry Andric       // If there is a non-sink block in UnknownBlocks with all jumps ignored,
88704eeddc0SDimitry Andric       // then we can't rebalance
88804eeddc0SDimitry Andric       if (NumIgnoredJumps == Block->SuccJumps.size())
8890eae32dcSDimitry Andric         return false;
8900eae32dcSDimitry Andric     }
8910eae32dcSDimitry Andric 
8920eae32dcSDimitry Andric     return true;
8930eae32dcSDimitry Andric   }
8940eae32dcSDimitry Andric 
89504eeddc0SDimitry Andric   /// Decide whether the Jump is ignored while processing an unknown subgraphs
89604eeddc0SDimitry Andric   /// rooted at basic block SrcBlock with the destination block, DstBlock.
89704eeddc0SDimitry Andric   bool ignoreJump(const FlowBlock *SrcBlock, const FlowBlock *DstBlock,
89804eeddc0SDimitry Andric                   const FlowJump *Jump) {
89904eeddc0SDimitry Andric     // Ignore unlikely jumps with zero flow
90004eeddc0SDimitry Andric     if (Jump->IsUnlikely && Jump->Flow == 0)
90104eeddc0SDimitry Andric       return true;
90204eeddc0SDimitry Andric 
90304eeddc0SDimitry Andric     auto JumpSource = &Func.Blocks[Jump->Source];
90404eeddc0SDimitry Andric     auto JumpTarget = &Func.Blocks[Jump->Target];
90504eeddc0SDimitry Andric 
90604eeddc0SDimitry Andric     // Do not ignore jumps coming into DstBlock
90704eeddc0SDimitry Andric     if (DstBlock != nullptr && JumpTarget == DstBlock)
90804eeddc0SDimitry Andric       return false;
90904eeddc0SDimitry Andric 
91004eeddc0SDimitry Andric     // Ignore jumps out of SrcBlock to known blocks
911bdd1243dSDimitry Andric     if (!JumpTarget->HasUnknownWeight && JumpSource == SrcBlock)
91204eeddc0SDimitry Andric       return true;
91304eeddc0SDimitry Andric 
91404eeddc0SDimitry Andric     // Ignore jumps to known blocks with zero flow
915bdd1243dSDimitry Andric     if (!JumpTarget->HasUnknownWeight && JumpTarget->Flow == 0)
91604eeddc0SDimitry Andric       return true;
91704eeddc0SDimitry Andric 
91804eeddc0SDimitry Andric     return false;
91904eeddc0SDimitry Andric   }
92004eeddc0SDimitry Andric 
9210eae32dcSDimitry Andric   /// Verify if the given unknown subgraph is acyclic, and if yes, reorder
92204eeddc0SDimitry Andric   /// UnknownBlocks in the topological order (so that all jumps are "forward").
92304eeddc0SDimitry Andric   bool isAcyclicSubgraph(const FlowBlock *SrcBlock, const FlowBlock *DstBlock,
92404eeddc0SDimitry Andric                          std::vector<FlowBlock *> &UnknownBlocks) {
9250eae32dcSDimitry Andric     // Extract local in-degrees in the considered subgraph
9260eae32dcSDimitry Andric     auto LocalInDegree = std::vector<uint64_t>(NumBlocks(), 0);
92704eeddc0SDimitry Andric     auto fillInDegree = [&](const FlowBlock *Block) {
928bdd1243dSDimitry Andric       for (auto *Jump : Block->SuccJumps) {
92904eeddc0SDimitry Andric         if (ignoreJump(SrcBlock, DstBlock, Jump))
93004eeddc0SDimitry Andric           continue;
9310eae32dcSDimitry Andric         LocalInDegree[Jump->Target]++;
9320eae32dcSDimitry Andric       }
93304eeddc0SDimitry Andric     };
93404eeddc0SDimitry Andric     fillInDegree(SrcBlock);
935bdd1243dSDimitry Andric     for (auto *Block : UnknownBlocks) {
93604eeddc0SDimitry Andric       fillInDegree(Block);
9370eae32dcSDimitry Andric     }
9380eae32dcSDimitry Andric     // A loop containing SrcBlock
9390eae32dcSDimitry Andric     if (LocalInDegree[SrcBlock->Index] > 0)
9400eae32dcSDimitry Andric       return false;
9410eae32dcSDimitry Andric 
9420eae32dcSDimitry Andric     std::vector<FlowBlock *> AcyclicOrder;
9430eae32dcSDimitry Andric     std::queue<uint64_t> Queue;
9440eae32dcSDimitry Andric     Queue.push(SrcBlock->Index);
9450eae32dcSDimitry Andric     while (!Queue.empty()) {
94604eeddc0SDimitry Andric       FlowBlock *Block = &Func.Blocks[Queue.front()];
9470eae32dcSDimitry Andric       Queue.pop();
94804eeddc0SDimitry Andric       // Stop propagation once we reach DstBlock, if any
94904eeddc0SDimitry Andric       if (DstBlock != nullptr && Block == DstBlock)
9500eae32dcSDimitry Andric         break;
9510eae32dcSDimitry Andric 
95204eeddc0SDimitry Andric       // Keep an acyclic order of unknown blocks
953bdd1243dSDimitry Andric       if (Block->HasUnknownWeight && Block != SrcBlock)
95404eeddc0SDimitry Andric         AcyclicOrder.push_back(Block);
95504eeddc0SDimitry Andric 
9560eae32dcSDimitry Andric       // Add to the queue all successors with zero local in-degree
957bdd1243dSDimitry Andric       for (auto *Jump : Block->SuccJumps) {
95804eeddc0SDimitry Andric         if (ignoreJump(SrcBlock, DstBlock, Jump))
95904eeddc0SDimitry Andric           continue;
9600eae32dcSDimitry Andric         uint64_t Dst = Jump->Target;
9610eae32dcSDimitry Andric         LocalInDegree[Dst]--;
9620eae32dcSDimitry Andric         if (LocalInDegree[Dst] == 0) {
9630eae32dcSDimitry Andric           Queue.push(Dst);
9640eae32dcSDimitry Andric         }
9650eae32dcSDimitry Andric       }
9660eae32dcSDimitry Andric     }
9670eae32dcSDimitry Andric 
9680eae32dcSDimitry Andric     // If there is a cycle in the subgraph, AcyclicOrder contains only a subset
9690eae32dcSDimitry Andric     // of all blocks
97004eeddc0SDimitry Andric     if (UnknownBlocks.size() != AcyclicOrder.size())
9710eae32dcSDimitry Andric       return false;
97204eeddc0SDimitry Andric     UnknownBlocks = AcyclicOrder;
9730eae32dcSDimitry Andric     return true;
9740eae32dcSDimitry Andric   }
9750eae32dcSDimitry Andric 
97604eeddc0SDimitry Andric   /// Rebalance a given subgraph rooted at SrcBlock, ending at DstBlock and
97704eeddc0SDimitry Andric   /// having UnknownBlocks intermediate blocks.
97804eeddc0SDimitry Andric   void rebalanceUnknownSubgraph(const FlowBlock *SrcBlock,
97904eeddc0SDimitry Andric                                 const FlowBlock *DstBlock,
98004eeddc0SDimitry Andric                                 const std::vector<FlowBlock *> &UnknownBlocks) {
9810eae32dcSDimitry Andric     assert(SrcBlock->Flow > 0 && "zero-flow block in unknown subgraph");
9820eae32dcSDimitry Andric 
98304eeddc0SDimitry Andric     // Ditribute flow from the source block
98404eeddc0SDimitry Andric     uint64_t BlockFlow = 0;
98504eeddc0SDimitry Andric     // SrcBlock's flow is the sum of outgoing flows along non-ignored jumps
986bdd1243dSDimitry Andric     for (auto *Jump : SrcBlock->SuccJumps) {
98704eeddc0SDimitry Andric       if (ignoreJump(SrcBlock, DstBlock, Jump))
9880eae32dcSDimitry Andric         continue;
98904eeddc0SDimitry Andric       BlockFlow += Jump->Flow;
9900eae32dcSDimitry Andric     }
99104eeddc0SDimitry Andric     rebalanceBlock(SrcBlock, DstBlock, SrcBlock, BlockFlow);
99204eeddc0SDimitry Andric 
99304eeddc0SDimitry Andric     // Ditribute flow from the remaining blocks
994bdd1243dSDimitry Andric     for (auto *Block : UnknownBlocks) {
995bdd1243dSDimitry Andric       assert(Block->HasUnknownWeight && "incorrect unknown subgraph");
99604eeddc0SDimitry Andric       uint64_t BlockFlow = 0;
99704eeddc0SDimitry Andric       // Block's flow is the sum of incoming flows
998bdd1243dSDimitry Andric       for (auto *Jump : Block->PredJumps) {
99904eeddc0SDimitry Andric         BlockFlow += Jump->Flow;
100004eeddc0SDimitry Andric       }
100104eeddc0SDimitry Andric       Block->Flow = BlockFlow;
100204eeddc0SDimitry Andric       rebalanceBlock(SrcBlock, DstBlock, Block, BlockFlow);
100304eeddc0SDimitry Andric     }
100404eeddc0SDimitry Andric   }
100504eeddc0SDimitry Andric 
100604eeddc0SDimitry Andric   /// Redistribute flow for a block in a subgraph rooted at SrcBlock,
100704eeddc0SDimitry Andric   /// and ending at DstBlock.
100804eeddc0SDimitry Andric   void rebalanceBlock(const FlowBlock *SrcBlock, const FlowBlock *DstBlock,
100904eeddc0SDimitry Andric                       const FlowBlock *Block, uint64_t BlockFlow) {
101004eeddc0SDimitry Andric     // Process all successor jumps and update corresponding flow values
101104eeddc0SDimitry Andric     size_t BlockDegree = 0;
1012bdd1243dSDimitry Andric     for (auto *Jump : Block->SuccJumps) {
101304eeddc0SDimitry Andric       if (ignoreJump(SrcBlock, DstBlock, Jump))
101404eeddc0SDimitry Andric         continue;
101504eeddc0SDimitry Andric       BlockDegree++;
101604eeddc0SDimitry Andric     }
101704eeddc0SDimitry Andric     // If all successor jumps of the block are ignored, skip it
101804eeddc0SDimitry Andric     if (DstBlock == nullptr && BlockDegree == 0)
101904eeddc0SDimitry Andric       return;
102004eeddc0SDimitry Andric     assert(BlockDegree > 0 && "all outgoing jumps are ignored");
102104eeddc0SDimitry Andric 
102204eeddc0SDimitry Andric     // Each of the Block's successors gets the following amount of flow.
102304eeddc0SDimitry Andric     // Rounding the value up so that all flow is propagated
102404eeddc0SDimitry Andric     uint64_t SuccFlow = (BlockFlow + BlockDegree - 1) / BlockDegree;
1025bdd1243dSDimitry Andric     for (auto *Jump : Block->SuccJumps) {
102604eeddc0SDimitry Andric       if (ignoreJump(SrcBlock, DstBlock, Jump))
102704eeddc0SDimitry Andric         continue;
102804eeddc0SDimitry Andric       uint64_t Flow = std::min(SuccFlow, BlockFlow);
10290eae32dcSDimitry Andric       Jump->Flow = Flow;
103004eeddc0SDimitry Andric       BlockFlow -= Flow;
10310eae32dcSDimitry Andric     }
103204eeddc0SDimitry Andric     assert(BlockFlow == 0 && "not all flow is propagated");
10330eae32dcSDimitry Andric   }
10340eae32dcSDimitry Andric 
10350eae32dcSDimitry Andric   /// A constant indicating an arbitrary exit block of a function.
10360eae32dcSDimitry Andric   static constexpr uint64_t AnyExitBlock = uint64_t(-1);
1037bdd1243dSDimitry Andric   /// Minimum BaseDistance for the jump distance values in island joining.
1038bdd1243dSDimitry Andric   static constexpr uint64_t MinBaseDistance = 10000;
10390eae32dcSDimitry Andric 
1040bdd1243dSDimitry Andric   /// Params for flow computation.
1041bdd1243dSDimitry Andric   const ProfiParams &Params;
10420eae32dcSDimitry Andric   /// The function.
10430eae32dcSDimitry Andric   FlowFunction &Func;
10440eae32dcSDimitry Andric };
10450eae32dcSDimitry Andric 
1046bdd1243dSDimitry Andric std::pair<int64_t, int64_t> assignBlockCosts(const ProfiParams &Params,
1047bdd1243dSDimitry Andric                                              const FlowBlock &Block);
1048bdd1243dSDimitry Andric std::pair<int64_t, int64_t> assignJumpCosts(const ProfiParams &Params,
1049bdd1243dSDimitry Andric                                             const FlowJump &Jump);
1050bdd1243dSDimitry Andric 
10514824e7fdSDimitry Andric /// Initializing flow network for a given function.
10524824e7fdSDimitry Andric ///
1053bdd1243dSDimitry Andric /// Every block is split into two nodes that are responsible for (i) an
1054bdd1243dSDimitry Andric /// incoming flow, (ii) an outgoing flow; they penalize an increase or a
10554824e7fdSDimitry Andric /// reduction of the block weight.
1056bdd1243dSDimitry Andric void initializeNetwork(const ProfiParams &Params, MinCostMaxFlow &Network,
1057bdd1243dSDimitry Andric                        FlowFunction &Func) {
10584824e7fdSDimitry Andric   uint64_t NumBlocks = Func.Blocks.size();
10594824e7fdSDimitry Andric   assert(NumBlocks > 1 && "Too few blocks in a function");
1060bdd1243dSDimitry Andric   uint64_t NumJumps = Func.Jumps.size();
1061bdd1243dSDimitry Andric   assert(NumJumps > 0 && "Too few jumps in a function");
10624824e7fdSDimitry Andric 
10634824e7fdSDimitry Andric   // Introducing dummy source/sink pairs to allow flow circulation.
1064*0fca6ea1SDimitry Andric   // The nodes corresponding to blocks of the function have indices in
1065bdd1243dSDimitry Andric   // the range [0 .. 2 * NumBlocks); the dummy sources/sinks are indexed by the
1066bdd1243dSDimitry Andric   // next four values.
1067bdd1243dSDimitry Andric   uint64_t S = 2 * NumBlocks;
10684824e7fdSDimitry Andric   uint64_t T = S + 1;
10694824e7fdSDimitry Andric   uint64_t S1 = S + 2;
10704824e7fdSDimitry Andric   uint64_t T1 = S + 3;
10714824e7fdSDimitry Andric 
1072bdd1243dSDimitry Andric   Network.initialize(2 * NumBlocks + 4, S1, T1);
10734824e7fdSDimitry Andric 
1074bdd1243dSDimitry Andric   // Initialize nodes of the flow network
10754824e7fdSDimitry Andric   for (uint64_t B = 0; B < NumBlocks; B++) {
10764824e7fdSDimitry Andric     auto &Block = Func.Blocks[B];
10774824e7fdSDimitry Andric 
1078bdd1243dSDimitry Andric     // Split every block into two auxiliary nodes to allow
1079bdd1243dSDimitry Andric     // increase/reduction of the block count.
1080bdd1243dSDimitry Andric     uint64_t Bin = 2 * B;
1081bdd1243dSDimitry Andric     uint64_t Bout = 2 * B + 1;
10824824e7fdSDimitry Andric 
10834824e7fdSDimitry Andric     // Edges from S and to T
10844824e7fdSDimitry Andric     if (Block.isEntry()) {
10854824e7fdSDimitry Andric       Network.addEdge(S, Bin, 0);
10864824e7fdSDimitry Andric     } else if (Block.isExit()) {
10874824e7fdSDimitry Andric       Network.addEdge(Bout, T, 0);
10884824e7fdSDimitry Andric     }
10894824e7fdSDimitry Andric 
1090bdd1243dSDimitry Andric     // Assign costs for increasing/decreasing the block counts
1091bdd1243dSDimitry Andric     auto [AuxCostInc, AuxCostDec] = assignBlockCosts(Params, Block);
10924824e7fdSDimitry Andric 
1093bdd1243dSDimitry Andric     // Add the corresponding edges to the network
1094bdd1243dSDimitry Andric     Network.addEdge(Bin, Bout, AuxCostInc);
10954824e7fdSDimitry Andric     if (Block.Weight > 0) {
1096bdd1243dSDimitry Andric       Network.addEdge(Bout, Bin, Block.Weight, AuxCostDec);
1097bdd1243dSDimitry Andric       Network.addEdge(S1, Bout, Block.Weight, 0);
1098bdd1243dSDimitry Andric       Network.addEdge(Bin, T1, Block.Weight, 0);
10994824e7fdSDimitry Andric     }
11004824e7fdSDimitry Andric   }
11014824e7fdSDimitry Andric 
1102bdd1243dSDimitry Andric   // Initialize edges of the flow network
1103bdd1243dSDimitry Andric   for (uint64_t J = 0; J < NumJumps; J++) {
1104bdd1243dSDimitry Andric     auto &Jump = Func.Jumps[J];
1105bdd1243dSDimitry Andric 
1106bdd1243dSDimitry Andric     // Get the endpoints corresponding to the jump
1107bdd1243dSDimitry Andric     uint64_t Jin = 2 * Jump.Source + 1;
1108bdd1243dSDimitry Andric     uint64_t Jout = 2 * Jump.Target;
1109bdd1243dSDimitry Andric 
1110bdd1243dSDimitry Andric     // Assign costs for increasing/decreasing the jump counts
1111bdd1243dSDimitry Andric     auto [AuxCostInc, AuxCostDec] = assignJumpCosts(Params, Jump);
1112bdd1243dSDimitry Andric 
1113bdd1243dSDimitry Andric     // Add the corresponding edges to the network
1114bdd1243dSDimitry Andric     Network.addEdge(Jin, Jout, AuxCostInc);
1115bdd1243dSDimitry Andric     if (Jump.Weight > 0) {
1116bdd1243dSDimitry Andric       Network.addEdge(Jout, Jin, Jump.Weight, AuxCostDec);
1117bdd1243dSDimitry Andric       Network.addEdge(S1, Jout, Jump.Weight, 0);
1118bdd1243dSDimitry Andric       Network.addEdge(Jin, T1, Jump.Weight, 0);
11194824e7fdSDimitry Andric     }
11204824e7fdSDimitry Andric   }
11214824e7fdSDimitry Andric 
11224824e7fdSDimitry Andric   // Make sure we have a valid flow circulation
11234824e7fdSDimitry Andric   Network.addEdge(T, S, 0);
11244824e7fdSDimitry Andric }
11254824e7fdSDimitry Andric 
1126bdd1243dSDimitry Andric /// Assign costs for increasing/decreasing the block counts.
1127bdd1243dSDimitry Andric std::pair<int64_t, int64_t> assignBlockCosts(const ProfiParams &Params,
1128bdd1243dSDimitry Andric                                              const FlowBlock &Block) {
1129bdd1243dSDimitry Andric   // Modifying the weight of an unlikely block is expensive
1130bdd1243dSDimitry Andric   if (Block.IsUnlikely)
1131bdd1243dSDimitry Andric     return std::make_pair(Params.CostUnlikely, Params.CostUnlikely);
11324824e7fdSDimitry Andric 
1133bdd1243dSDimitry Andric   // Assign default values for the costs
1134bdd1243dSDimitry Andric   int64_t CostInc = Params.CostBlockInc;
1135bdd1243dSDimitry Andric   int64_t CostDec = Params.CostBlockDec;
1136bdd1243dSDimitry Andric   // Update the costs depending on the block metadata
1137bdd1243dSDimitry Andric   if (Block.HasUnknownWeight) {
1138bdd1243dSDimitry Andric     CostInc = Params.CostBlockUnknownInc;
1139bdd1243dSDimitry Andric     CostDec = 0;
1140bdd1243dSDimitry Andric   } else {
1141bdd1243dSDimitry Andric     // Increasing the count for "cold" blocks with zero initial count is more
1142bdd1243dSDimitry Andric     // expensive than for "hot" ones
1143bdd1243dSDimitry Andric     if (Block.Weight == 0)
1144bdd1243dSDimitry Andric       CostInc = Params.CostBlockZeroInc;
1145bdd1243dSDimitry Andric     // Modifying the count of the entry block is expensive
1146bdd1243dSDimitry Andric     if (Block.isEntry()) {
1147bdd1243dSDimitry Andric       CostInc = Params.CostBlockEntryInc;
1148bdd1243dSDimitry Andric       CostDec = Params.CostBlockEntryDec;
11494824e7fdSDimitry Andric     }
11504824e7fdSDimitry Andric   }
1151bdd1243dSDimitry Andric   return std::make_pair(CostInc, CostDec);
11524824e7fdSDimitry Andric }
11534824e7fdSDimitry Andric 
1154bdd1243dSDimitry Andric /// Assign costs for increasing/decreasing the jump counts.
1155bdd1243dSDimitry Andric std::pair<int64_t, int64_t> assignJumpCosts(const ProfiParams &Params,
1156bdd1243dSDimitry Andric                                             const FlowJump &Jump) {
1157bdd1243dSDimitry Andric   // Modifying the weight of an unlikely jump is expensive
1158bdd1243dSDimitry Andric   if (Jump.IsUnlikely)
1159bdd1243dSDimitry Andric     return std::make_pair(Params.CostUnlikely, Params.CostUnlikely);
1160bdd1243dSDimitry Andric 
1161bdd1243dSDimitry Andric   // Assign default values for the costs
1162bdd1243dSDimitry Andric   int64_t CostInc = Params.CostJumpInc;
1163bdd1243dSDimitry Andric   int64_t CostDec = Params.CostJumpDec;
1164bdd1243dSDimitry Andric   // Update the costs depending on the block metadata
1165bdd1243dSDimitry Andric   if (Jump.Source + 1 == Jump.Target) {
1166bdd1243dSDimitry Andric     // Adjusting the fall-through branch
1167bdd1243dSDimitry Andric     CostInc = Params.CostJumpFTInc;
1168bdd1243dSDimitry Andric     CostDec = Params.CostJumpFTDec;
1169bdd1243dSDimitry Andric   }
1170bdd1243dSDimitry Andric   if (Jump.HasUnknownWeight) {
1171bdd1243dSDimitry Andric     // The cost is different for fall-through and non-fall-through branches
1172bdd1243dSDimitry Andric     if (Jump.Source + 1 == Jump.Target)
1173bdd1243dSDimitry Andric       CostInc = Params.CostJumpUnknownFTInc;
1174bdd1243dSDimitry Andric     else
1175bdd1243dSDimitry Andric       CostInc = Params.CostJumpUnknownInc;
1176bdd1243dSDimitry Andric     CostDec = 0;
1177bdd1243dSDimitry Andric   } else {
1178bdd1243dSDimitry Andric     assert(Jump.Weight > 0 && "found zero-weight jump with a positive weight");
1179bdd1243dSDimitry Andric   }
1180bdd1243dSDimitry Andric   return std::make_pair(CostInc, CostDec);
1181bdd1243dSDimitry Andric }
1182bdd1243dSDimitry Andric 
1183bdd1243dSDimitry Andric /// Extract resulting block and edge counts from the flow network.
1184bdd1243dSDimitry Andric void extractWeights(const ProfiParams &Params, MinCostMaxFlow &Network,
1185bdd1243dSDimitry Andric                     FlowFunction &Func) {
1186bdd1243dSDimitry Andric   uint64_t NumBlocks = Func.Blocks.size();
1187bdd1243dSDimitry Andric   uint64_t NumJumps = Func.Jumps.size();
1188bdd1243dSDimitry Andric 
11894824e7fdSDimitry Andric   // Extract resulting jump counts
1190bdd1243dSDimitry Andric   for (uint64_t J = 0; J < NumJumps; J++) {
1191bdd1243dSDimitry Andric     auto &Jump = Func.Jumps[J];
1192bdd1243dSDimitry Andric     uint64_t SrcOut = 2 * Jump.Source + 1;
1193bdd1243dSDimitry Andric     uint64_t DstIn = 2 * Jump.Target;
1194bdd1243dSDimitry Andric 
11954824e7fdSDimitry Andric     int64_t Flow = 0;
1196bdd1243dSDimitry Andric     int64_t AuxFlow = Network.getFlow(SrcOut, DstIn);
1197bdd1243dSDimitry Andric     if (Jump.Source != Jump.Target)
1198bdd1243dSDimitry Andric       Flow = int64_t(Jump.Weight) + AuxFlow;
1199bdd1243dSDimitry Andric     else
1200bdd1243dSDimitry Andric       Flow = int64_t(Jump.Weight) + (AuxFlow > 0 ? AuxFlow : 0);
1201bdd1243dSDimitry Andric 
12024824e7fdSDimitry Andric     Jump.Flow = Flow;
12034824e7fdSDimitry Andric     assert(Flow >= 0 && "negative jump flow");
12044824e7fdSDimitry Andric   }
1205bdd1243dSDimitry Andric 
1206bdd1243dSDimitry Andric   // Extract resulting block counts
1207bdd1243dSDimitry Andric   auto InFlow = std::vector<uint64_t>(NumBlocks, 0);
1208bdd1243dSDimitry Andric   auto OutFlow = std::vector<uint64_t>(NumBlocks, 0);
1209bdd1243dSDimitry Andric   for (auto &Jump : Func.Jumps) {
1210bdd1243dSDimitry Andric     InFlow[Jump.Target] += Jump.Flow;
1211bdd1243dSDimitry Andric     OutFlow[Jump.Source] += Jump.Flow;
1212bdd1243dSDimitry Andric   }
1213bdd1243dSDimitry Andric   for (uint64_t B = 0; B < NumBlocks; B++) {
1214bdd1243dSDimitry Andric     auto &Block = Func.Blocks[B];
1215bdd1243dSDimitry Andric     Block.Flow = std::max(OutFlow[B], InFlow[B]);
1216bdd1243dSDimitry Andric   }
12174824e7fdSDimitry Andric }
12184824e7fdSDimitry Andric 
12194824e7fdSDimitry Andric #ifndef NDEBUG
1220bdd1243dSDimitry Andric /// Verify that the provided block/jump weights are as expected.
1221bdd1243dSDimitry Andric void verifyInput(const FlowFunction &Func) {
122206c3fb27SDimitry Andric   // Verify entry and exit blocks
1223bdd1243dSDimitry Andric   assert(Func.Entry == 0 && Func.Blocks[0].isEntry());
122406c3fb27SDimitry Andric   size_t NumExitBlocks = 0;
1225bdd1243dSDimitry Andric   for (size_t I = 1; I < Func.Blocks.size(); I++) {
1226bdd1243dSDimitry Andric     assert(!Func.Blocks[I].isEntry() && "multiple entry blocks");
122706c3fb27SDimitry Andric     if (Func.Blocks[I].isExit())
122806c3fb27SDimitry Andric       NumExitBlocks++;
122906c3fb27SDimitry Andric   }
123006c3fb27SDimitry Andric   assert(NumExitBlocks > 0 && "cannot find exit blocks");
123106c3fb27SDimitry Andric 
123206c3fb27SDimitry Andric   // Verify that there are no parallel edges
123306c3fb27SDimitry Andric   for (auto &Block : Func.Blocks) {
123406c3fb27SDimitry Andric     std::unordered_set<uint64_t> UniqueSuccs;
123506c3fb27SDimitry Andric     for (auto &Jump : Block.SuccJumps) {
123606c3fb27SDimitry Andric       auto It = UniqueSuccs.insert(Jump->Target);
123706c3fb27SDimitry Andric       assert(It.second && "input CFG contains parallel edges");
123806c3fb27SDimitry Andric     }
1239bdd1243dSDimitry Andric   }
1240bdd1243dSDimitry Andric   // Verify CFG jumps
1241bdd1243dSDimitry Andric   for (auto &Block : Func.Blocks) {
1242bdd1243dSDimitry Andric     assert((!Block.isEntry() || !Block.isExit()) &&
1243bdd1243dSDimitry Andric            "a block cannot be an entry and an exit");
1244bdd1243dSDimitry Andric   }
1245bdd1243dSDimitry Andric   // Verify input block weights
1246bdd1243dSDimitry Andric   for (auto &Block : Func.Blocks) {
1247bdd1243dSDimitry Andric     assert((!Block.HasUnknownWeight || Block.Weight == 0 || Block.isEntry()) &&
1248bdd1243dSDimitry Andric            "non-zero weight of a block w/o weight except for an entry");
1249bdd1243dSDimitry Andric   }
1250bdd1243dSDimitry Andric   // Verify input jump weights
1251bdd1243dSDimitry Andric   for (auto &Jump : Func.Jumps) {
1252bdd1243dSDimitry Andric     assert((!Jump.HasUnknownWeight || Jump.Weight == 0) &&
1253bdd1243dSDimitry Andric            "non-zero weight of a jump w/o weight");
1254bdd1243dSDimitry Andric   }
1255bdd1243dSDimitry Andric }
1256bdd1243dSDimitry Andric 
1257bdd1243dSDimitry Andric /// Verify that the computed flow values satisfy flow conservation rules.
1258bdd1243dSDimitry Andric void verifyOutput(const FlowFunction &Func) {
12594824e7fdSDimitry Andric   const uint64_t NumBlocks = Func.Blocks.size();
12604824e7fdSDimitry Andric   auto InFlow = std::vector<uint64_t>(NumBlocks, 0);
12614824e7fdSDimitry Andric   auto OutFlow = std::vector<uint64_t>(NumBlocks, 0);
1262bdd1243dSDimitry Andric   for (const auto &Jump : Func.Jumps) {
12634824e7fdSDimitry Andric     InFlow[Jump.Target] += Jump.Flow;
12644824e7fdSDimitry Andric     OutFlow[Jump.Source] += Jump.Flow;
12654824e7fdSDimitry Andric   }
12664824e7fdSDimitry Andric 
12674824e7fdSDimitry Andric   uint64_t TotalInFlow = 0;
12684824e7fdSDimitry Andric   uint64_t TotalOutFlow = 0;
12694824e7fdSDimitry Andric   for (uint64_t I = 0; I < NumBlocks; I++) {
12704824e7fdSDimitry Andric     auto &Block = Func.Blocks[I];
12714824e7fdSDimitry Andric     if (Block.isEntry()) {
12724824e7fdSDimitry Andric       TotalInFlow += Block.Flow;
12734824e7fdSDimitry Andric       assert(Block.Flow == OutFlow[I] && "incorrectly computed control flow");
12744824e7fdSDimitry Andric     } else if (Block.isExit()) {
12754824e7fdSDimitry Andric       TotalOutFlow += Block.Flow;
12764824e7fdSDimitry Andric       assert(Block.Flow == InFlow[I] && "incorrectly computed control flow");
12774824e7fdSDimitry Andric     } else {
12784824e7fdSDimitry Andric       assert(Block.Flow == OutFlow[I] && "incorrectly computed control flow");
12794824e7fdSDimitry Andric       assert(Block.Flow == InFlow[I] && "incorrectly computed control flow");
12804824e7fdSDimitry Andric     }
12814824e7fdSDimitry Andric   }
12824824e7fdSDimitry Andric   assert(TotalInFlow == TotalOutFlow && "incorrectly computed control flow");
12830eae32dcSDimitry Andric 
12840eae32dcSDimitry Andric   // Verify that there are no isolated flow components
12850eae32dcSDimitry Andric   // One could modify FlowFunction to hold edges indexed by the sources, which
12860eae32dcSDimitry Andric   // will avoid a creation of the object
12870eae32dcSDimitry Andric   auto PositiveFlowEdges = std::vector<std::vector<uint64_t>>(NumBlocks);
1288bdd1243dSDimitry Andric   for (const auto &Jump : Func.Jumps) {
12890eae32dcSDimitry Andric     if (Jump.Flow > 0) {
12900eae32dcSDimitry Andric       PositiveFlowEdges[Jump.Source].push_back(Jump.Target);
12910eae32dcSDimitry Andric     }
12920eae32dcSDimitry Andric   }
12930eae32dcSDimitry Andric 
12940eae32dcSDimitry Andric   // Run BFS from the source along edges with positive flow
12950eae32dcSDimitry Andric   std::queue<uint64_t> Queue;
129604eeddc0SDimitry Andric   auto Visited = BitVector(NumBlocks, false);
12970eae32dcSDimitry Andric   Queue.push(Func.Entry);
12980eae32dcSDimitry Andric   Visited[Func.Entry] = true;
12990eae32dcSDimitry Andric   while (!Queue.empty()) {
13000eae32dcSDimitry Andric     uint64_t Src = Queue.front();
13010eae32dcSDimitry Andric     Queue.pop();
13020eae32dcSDimitry Andric     for (uint64_t Dst : PositiveFlowEdges[Src]) {
13030eae32dcSDimitry Andric       if (!Visited[Dst]) {
13040eae32dcSDimitry Andric         Queue.push(Dst);
13050eae32dcSDimitry Andric         Visited[Dst] = true;
13060eae32dcSDimitry Andric       }
13070eae32dcSDimitry Andric     }
13080eae32dcSDimitry Andric   }
13090eae32dcSDimitry Andric 
13100eae32dcSDimitry Andric   // Verify that every block that has a positive flow is reached from the source
13110eae32dcSDimitry Andric   // along edges with a positive flow
13120eae32dcSDimitry Andric   for (uint64_t I = 0; I < NumBlocks; I++) {
13130eae32dcSDimitry Andric     auto &Block = Func.Blocks[I];
13140eae32dcSDimitry Andric     assert((Visited[I] || Block.Flow == 0) && "an isolated flow component");
13150eae32dcSDimitry Andric   }
13164824e7fdSDimitry Andric }
13174824e7fdSDimitry Andric #endif
13184824e7fdSDimitry Andric 
13194824e7fdSDimitry Andric } // end of anonymous namespace
13204824e7fdSDimitry Andric 
132106c3fb27SDimitry Andric /// Apply the profile inference algorithm for a given function and provided
132206c3fb27SDimitry Andric /// profi options
1323bdd1243dSDimitry Andric void llvm::applyFlowInference(const ProfiParams &Params, FlowFunction &Func) {
132406c3fb27SDimitry Andric   // Check if the function has samples and assign initial flow values
132506c3fb27SDimitry Andric   bool HasSamples = false;
132606c3fb27SDimitry Andric   for (FlowBlock &Block : Func.Blocks) {
132706c3fb27SDimitry Andric     if (Block.Weight > 0)
132806c3fb27SDimitry Andric       HasSamples = true;
132906c3fb27SDimitry Andric     Block.Flow = Block.Weight;
133006c3fb27SDimitry Andric   }
133106c3fb27SDimitry Andric   for (FlowJump &Jump : Func.Jumps) {
133206c3fb27SDimitry Andric     if (Jump.Weight > 0)
133306c3fb27SDimitry Andric       HasSamples = true;
133406c3fb27SDimitry Andric     Jump.Flow = Jump.Weight;
133506c3fb27SDimitry Andric   }
133606c3fb27SDimitry Andric 
133706c3fb27SDimitry Andric   // Quit early for functions with a single block or ones w/o samples
133806c3fb27SDimitry Andric   if (Func.Blocks.size() <= 1 || !HasSamples)
133906c3fb27SDimitry Andric     return;
134006c3fb27SDimitry Andric 
1341bdd1243dSDimitry Andric #ifndef NDEBUG
1342bdd1243dSDimitry Andric   // Verify the input data
1343bdd1243dSDimitry Andric   verifyInput(Func);
1344bdd1243dSDimitry Andric #endif
1345bdd1243dSDimitry Andric 
13464824e7fdSDimitry Andric   // Create and apply an inference network model
1347bdd1243dSDimitry Andric   auto InferenceNetwork = MinCostMaxFlow(Params);
1348bdd1243dSDimitry Andric   initializeNetwork(Params, InferenceNetwork, Func);
13494824e7fdSDimitry Andric   InferenceNetwork.run();
13504824e7fdSDimitry Andric 
13514824e7fdSDimitry Andric   // Extract flow values for every block and every edge
1352bdd1243dSDimitry Andric   extractWeights(Params, InferenceNetwork, Func);
13534824e7fdSDimitry Andric 
13540eae32dcSDimitry Andric   // Post-processing adjustments to the flow
1355bdd1243dSDimitry Andric   auto Adjuster = FlowAdjuster(Params, Func);
13560eae32dcSDimitry Andric   Adjuster.run();
13570eae32dcSDimitry Andric 
13584824e7fdSDimitry Andric #ifndef NDEBUG
13594824e7fdSDimitry Andric   // Verify the result
1360bdd1243dSDimitry Andric   verifyOutput(Func);
13614824e7fdSDimitry Andric #endif
13624824e7fdSDimitry Andric }
1363bdd1243dSDimitry Andric 
1364bdd1243dSDimitry Andric /// Apply the profile inference algorithm for a given flow function
1365bdd1243dSDimitry Andric void llvm::applyFlowInference(FlowFunction &Func) {
1366bdd1243dSDimitry Andric   ProfiParams Params;
1367bdd1243dSDimitry Andric   // Set the params from the command-line flags.
1368bdd1243dSDimitry Andric   Params.EvenFlowDistribution = SampleProfileEvenFlowDistribution;
1369bdd1243dSDimitry Andric   Params.RebalanceUnknown = SampleProfileRebalanceUnknown;
1370bdd1243dSDimitry Andric   Params.JoinIslands = SampleProfileJoinIslands;
1371bdd1243dSDimitry Andric   Params.CostBlockInc = SampleProfileProfiCostBlockInc;
1372bdd1243dSDimitry Andric   Params.CostBlockDec = SampleProfileProfiCostBlockDec;
1373bdd1243dSDimitry Andric   Params.CostBlockEntryInc = SampleProfileProfiCostBlockEntryInc;
1374bdd1243dSDimitry Andric   Params.CostBlockEntryDec = SampleProfileProfiCostBlockEntryDec;
1375bdd1243dSDimitry Andric   Params.CostBlockZeroInc = SampleProfileProfiCostBlockZeroInc;
1376bdd1243dSDimitry Andric   Params.CostBlockUnknownInc = SampleProfileProfiCostBlockUnknownInc;
1377bdd1243dSDimitry Andric 
1378bdd1243dSDimitry Andric   applyFlowInference(Params, Func);
1379bdd1243dSDimitry Andric }
1380