17cc2493dSspupyrev //===- SampleProfileInference.cpp - Adjust sample profiles in the IR ------===// 27cc2493dSspupyrev // 37cc2493dSspupyrev // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 47cc2493dSspupyrev // See https://llvm.org/LICENSE.txt for license information. 57cc2493dSspupyrev // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 67cc2493dSspupyrev // 77cc2493dSspupyrev //===----------------------------------------------------------------------===// 87cc2493dSspupyrev // 97cc2493dSspupyrev // This file implements a profile inference algorithm. Given an incomplete and 107cc2493dSspupyrev // possibly imprecise block counts, the algorithm reconstructs realistic block 117cc2493dSspupyrev // and edge counts that satisfy flow conservation rules, while minimally modify 127cc2493dSspupyrev // input block counts. 137cc2493dSspupyrev // 147cc2493dSspupyrev //===----------------------------------------------------------------------===// 157cc2493dSspupyrev 167cc2493dSspupyrev #include "llvm/Transforms/Utils/SampleProfileInference.h" 175f4ae564SJan Svoboda #include "llvm/ADT/BitVector.h" 187cc2493dSspupyrev #include "llvm/Support/Debug.h" 197cc2493dSspupyrev #include <queue> 207cc2493dSspupyrev #include <set> 217cc2493dSspupyrev 227cc2493dSspupyrev using namespace llvm; 237cc2493dSspupyrev #define DEBUG_TYPE "sample-profile-inference" 247cc2493dSspupyrev 257cc2493dSspupyrev namespace { 267cc2493dSspupyrev 277cc2493dSspupyrev /// A value indicating an infinite flow/capacity/weight of a block/edge. 287cc2493dSspupyrev /// Not using numeric_limits<int64_t>::max(), as the values can be summed up 297cc2493dSspupyrev /// during the execution. 307cc2493dSspupyrev static constexpr int64_t INF = ((int64_t)1) << 50; 317cc2493dSspupyrev 327cc2493dSspupyrev /// The minimum-cost maximum flow algorithm. 337cc2493dSspupyrev /// 347cc2493dSspupyrev /// The algorithm finds the maximum flow of minimum cost on a given (directed) 357cc2493dSspupyrev /// network using a modified version of the classical Moore-Bellman-Ford 367cc2493dSspupyrev /// approach. The algorithm applies a number of augmentation iterations in which 377cc2493dSspupyrev /// flow is sent along paths of positive capacity from the source to the sink. 387cc2493dSspupyrev /// The worst-case time complexity of the implementation is O(v(f)*m*n), where 397cc2493dSspupyrev /// where m is the number of edges, n is the number of vertices, and v(f) is the 407cc2493dSspupyrev /// value of the maximum flow. However, the observed running time on typical 417cc2493dSspupyrev /// instances is sub-quadratic, that is, o(n^2). 427cc2493dSspupyrev /// 437cc2493dSspupyrev /// The input is a set of edges with specified costs and capacities, and a pair 447cc2493dSspupyrev /// of nodes (source and sink). The output is the flow along each edge of the 457cc2493dSspupyrev /// minimum total cost respecting the given edge capacities. 467cc2493dSspupyrev class MinCostMaxFlow { 477cc2493dSspupyrev public: 487cc2493dSspupyrev // Initialize algorithm's data structures for a network of a given size. 497cc2493dSspupyrev void initialize(uint64_t NodeCount, uint64_t SourceNode, uint64_t SinkNode) { 507cc2493dSspupyrev Source = SourceNode; 517cc2493dSspupyrev Target = SinkNode; 527cc2493dSspupyrev 537cc2493dSspupyrev Nodes = std::vector<Node>(NodeCount); 547cc2493dSspupyrev Edges = std::vector<std::vector<Edge>>(NodeCount, std::vector<Edge>()); 557cc2493dSspupyrev } 567cc2493dSspupyrev 577cc2493dSspupyrev // Run the algorithm. 587cc2493dSspupyrev int64_t run() { 597cc2493dSspupyrev // Find an augmenting path and update the flow along the path 607cc2493dSspupyrev size_t AugmentationIters = 0; 617cc2493dSspupyrev while (findAugmentingPath()) { 627cc2493dSspupyrev augmentFlowAlongPath(); 637cc2493dSspupyrev AugmentationIters++; 647cc2493dSspupyrev } 657cc2493dSspupyrev 667cc2493dSspupyrev // Compute the total flow and its cost 677cc2493dSspupyrev int64_t TotalCost = 0; 687cc2493dSspupyrev int64_t TotalFlow = 0; 697cc2493dSspupyrev for (uint64_t Src = 0; Src < Nodes.size(); Src++) { 707cc2493dSspupyrev for (auto &Edge : Edges[Src]) { 717cc2493dSspupyrev if (Edge.Flow > 0) { 727cc2493dSspupyrev TotalCost += Edge.Cost * Edge.Flow; 737cc2493dSspupyrev if (Src == Source) 747cc2493dSspupyrev TotalFlow += Edge.Flow; 757cc2493dSspupyrev } 767cc2493dSspupyrev } 777cc2493dSspupyrev } 787cc2493dSspupyrev LLVM_DEBUG(dbgs() << "Completed profi after " << AugmentationIters 797cc2493dSspupyrev << " iterations with " << TotalFlow << " total flow" 807cc2493dSspupyrev << " of " << TotalCost << " cost\n"); 8122d82949SKazu Hirata (void)TotalFlow; 827cc2493dSspupyrev return TotalCost; 837cc2493dSspupyrev } 847cc2493dSspupyrev 857cc2493dSspupyrev /// Adding an edge to the network with a specified capacity and a cost. 867cc2493dSspupyrev /// Multiple edges between a pair of nodes are allowed but self-edges 877cc2493dSspupyrev /// are not supported. 887cc2493dSspupyrev void addEdge(uint64_t Src, uint64_t Dst, int64_t Capacity, int64_t Cost) { 897cc2493dSspupyrev assert(Capacity > 0 && "adding an edge of zero capacity"); 907cc2493dSspupyrev assert(Src != Dst && "loop edge are not supported"); 917cc2493dSspupyrev 927cc2493dSspupyrev Edge SrcEdge; 937cc2493dSspupyrev SrcEdge.Dst = Dst; 947cc2493dSspupyrev SrcEdge.Cost = Cost; 957cc2493dSspupyrev SrcEdge.Capacity = Capacity; 967cc2493dSspupyrev SrcEdge.Flow = 0; 977cc2493dSspupyrev SrcEdge.RevEdgeIndex = Edges[Dst].size(); 987cc2493dSspupyrev 997cc2493dSspupyrev Edge DstEdge; 1007cc2493dSspupyrev DstEdge.Dst = Src; 1017cc2493dSspupyrev DstEdge.Cost = -Cost; 1027cc2493dSspupyrev DstEdge.Capacity = 0; 1037cc2493dSspupyrev DstEdge.Flow = 0; 1047cc2493dSspupyrev DstEdge.RevEdgeIndex = Edges[Src].size(); 1057cc2493dSspupyrev 1067cc2493dSspupyrev Edges[Src].push_back(SrcEdge); 1077cc2493dSspupyrev Edges[Dst].push_back(DstEdge); 1087cc2493dSspupyrev } 1097cc2493dSspupyrev 1107cc2493dSspupyrev /// Adding an edge to the network of infinite capacity and a given cost. 1117cc2493dSspupyrev void addEdge(uint64_t Src, uint64_t Dst, int64_t Cost) { 1127cc2493dSspupyrev addEdge(Src, Dst, INF, Cost); 1137cc2493dSspupyrev } 1147cc2493dSspupyrev 1157cc2493dSspupyrev /// Get the total flow from a given source node. 1167cc2493dSspupyrev /// Returns a list of pairs (target node, amount of flow to the target). 1177cc2493dSspupyrev const std::vector<std::pair<uint64_t, int64_t>> getFlow(uint64_t Src) const { 1187cc2493dSspupyrev std::vector<std::pair<uint64_t, int64_t>> Flow; 1197cc2493dSspupyrev for (auto &Edge : Edges[Src]) { 1207cc2493dSspupyrev if (Edge.Flow > 0) 1217cc2493dSspupyrev Flow.push_back(std::make_pair(Edge.Dst, Edge.Flow)); 1227cc2493dSspupyrev } 1237cc2493dSspupyrev return Flow; 1247cc2493dSspupyrev } 1257cc2493dSspupyrev 1267cc2493dSspupyrev /// Get the total flow between a pair of nodes. 1277cc2493dSspupyrev int64_t getFlow(uint64_t Src, uint64_t Dst) const { 1287cc2493dSspupyrev int64_t Flow = 0; 1297cc2493dSspupyrev for (auto &Edge : Edges[Src]) { 1307cc2493dSspupyrev if (Edge.Dst == Dst) { 1317cc2493dSspupyrev Flow += Edge.Flow; 1327cc2493dSspupyrev } 1337cc2493dSspupyrev } 1347cc2493dSspupyrev return Flow; 1357cc2493dSspupyrev } 1367cc2493dSspupyrev 1377cc2493dSspupyrev /// A cost of increasing a block's count by one. 1387cc2493dSspupyrev static constexpr int64_t AuxCostInc = 10; 1397cc2493dSspupyrev /// A cost of decreasing a block's count by one. 1407cc2493dSspupyrev static constexpr int64_t AuxCostDec = 20; 1417cc2493dSspupyrev /// A cost of increasing a count of zero-weight block by one. 1427cc2493dSspupyrev static constexpr int64_t AuxCostIncZero = 11; 1437cc2493dSspupyrev /// A cost of increasing the entry block's count by one. 1447cc2493dSspupyrev static constexpr int64_t AuxCostIncEntry = 40; 1457cc2493dSspupyrev /// A cost of decreasing the entry block's count by one. 1467cc2493dSspupyrev static constexpr int64_t AuxCostDecEntry = 10; 1477cc2493dSspupyrev /// A cost of taking an unlikely jump. 148*13d1364aSspupyrev static constexpr int64_t AuxCostUnlikely = ((int64_t)1) << 30; 1497cc2493dSspupyrev 1507cc2493dSspupyrev private: 1517cc2493dSspupyrev /// Check for existence of an augmenting path with a positive capacity. 1527cc2493dSspupyrev bool findAugmentingPath() { 1537cc2493dSspupyrev // Initialize data structures 1547cc2493dSspupyrev for (auto &Node : Nodes) { 1557cc2493dSspupyrev Node.Distance = INF; 1567cc2493dSspupyrev Node.ParentNode = uint64_t(-1); 1577cc2493dSspupyrev Node.ParentEdgeIndex = uint64_t(-1); 1587cc2493dSspupyrev Node.Taken = false; 1597cc2493dSspupyrev } 1607cc2493dSspupyrev 1617cc2493dSspupyrev std::queue<uint64_t> Queue; 1627cc2493dSspupyrev Queue.push(Source); 1637cc2493dSspupyrev Nodes[Source].Distance = 0; 1647cc2493dSspupyrev Nodes[Source].Taken = true; 1657cc2493dSspupyrev while (!Queue.empty()) { 1667cc2493dSspupyrev uint64_t Src = Queue.front(); 1677cc2493dSspupyrev Queue.pop(); 1687cc2493dSspupyrev Nodes[Src].Taken = false; 1697cc2493dSspupyrev // Although the residual network contains edges with negative costs 1707cc2493dSspupyrev // (in particular, backward edges), it can be shown that there are no 1717cc2493dSspupyrev // negative-weight cycles and the following two invariants are maintained: 1727cc2493dSspupyrev // (i) Dist[Source, V] >= 0 and (ii) Dist[V, Target] >= 0 for all nodes V, 1737cc2493dSspupyrev // where Dist is the length of the shortest path between two nodes. This 1747cc2493dSspupyrev // allows to prune the search-space of the path-finding algorithm using 1757cc2493dSspupyrev // the following early-stop criteria: 1767cc2493dSspupyrev // -- If we find a path with zero-distance from Source to Target, stop the 1777cc2493dSspupyrev // search, as the path is the shortest since Dist[Source, Target] >= 0; 1787cc2493dSspupyrev // -- If we have Dist[Source, V] > Dist[Source, Target], then do not 1797cc2493dSspupyrev // process node V, as it is guaranteed _not_ to be on a shortest path 1807cc2493dSspupyrev // from Source to Target; it follows from inequalities 1817cc2493dSspupyrev // Dist[Source, Target] >= Dist[Source, V] + Dist[V, Target] 1827cc2493dSspupyrev // >= Dist[Source, V] 1837cc2493dSspupyrev if (Nodes[Target].Distance == 0) 1847cc2493dSspupyrev break; 1857cc2493dSspupyrev if (Nodes[Src].Distance > Nodes[Target].Distance) 1867cc2493dSspupyrev continue; 1877cc2493dSspupyrev 1887cc2493dSspupyrev // Process adjacent edges 1897cc2493dSspupyrev for (uint64_t EdgeIdx = 0; EdgeIdx < Edges[Src].size(); EdgeIdx++) { 1907cc2493dSspupyrev auto &Edge = Edges[Src][EdgeIdx]; 1917cc2493dSspupyrev if (Edge.Flow < Edge.Capacity) { 1927cc2493dSspupyrev uint64_t Dst = Edge.Dst; 1937cc2493dSspupyrev int64_t NewDistance = Nodes[Src].Distance + Edge.Cost; 1947cc2493dSspupyrev if (Nodes[Dst].Distance > NewDistance) { 1957cc2493dSspupyrev // Update the distance and the parent node/edge 1967cc2493dSspupyrev Nodes[Dst].Distance = NewDistance; 1977cc2493dSspupyrev Nodes[Dst].ParentNode = Src; 1987cc2493dSspupyrev Nodes[Dst].ParentEdgeIndex = EdgeIdx; 1997cc2493dSspupyrev // Add the node to the queue, if it is not there yet 2007cc2493dSspupyrev if (!Nodes[Dst].Taken) { 2017cc2493dSspupyrev Queue.push(Dst); 2027cc2493dSspupyrev Nodes[Dst].Taken = true; 2037cc2493dSspupyrev } 2047cc2493dSspupyrev } 2057cc2493dSspupyrev } 2067cc2493dSspupyrev } 2077cc2493dSspupyrev } 2087cc2493dSspupyrev 2097cc2493dSspupyrev return Nodes[Target].Distance != INF; 2107cc2493dSspupyrev } 2117cc2493dSspupyrev 2127cc2493dSspupyrev /// Update the current flow along the augmenting path. 2137cc2493dSspupyrev void augmentFlowAlongPath() { 2147cc2493dSspupyrev // Find path capacity 2157cc2493dSspupyrev int64_t PathCapacity = INF; 2167cc2493dSspupyrev uint64_t Now = Target; 2177cc2493dSspupyrev while (Now != Source) { 2187cc2493dSspupyrev uint64_t Pred = Nodes[Now].ParentNode; 2197cc2493dSspupyrev auto &Edge = Edges[Pred][Nodes[Now].ParentEdgeIndex]; 2207cc2493dSspupyrev PathCapacity = std::min(PathCapacity, Edge.Capacity - Edge.Flow); 2217cc2493dSspupyrev Now = Pred; 2227cc2493dSspupyrev } 2237cc2493dSspupyrev 22493a2c291Sspupyrev assert(PathCapacity > 0 && "found an incorrect augmenting path"); 2257cc2493dSspupyrev 2267cc2493dSspupyrev // Update the flow along the path 2277cc2493dSspupyrev Now = Target; 2287cc2493dSspupyrev while (Now != Source) { 2297cc2493dSspupyrev uint64_t Pred = Nodes[Now].ParentNode; 2307cc2493dSspupyrev auto &Edge = Edges[Pred][Nodes[Now].ParentEdgeIndex]; 2317cc2493dSspupyrev auto &RevEdge = Edges[Now][Edge.RevEdgeIndex]; 2327cc2493dSspupyrev 2337cc2493dSspupyrev Edge.Flow += PathCapacity; 2347cc2493dSspupyrev RevEdge.Flow -= PathCapacity; 2357cc2493dSspupyrev 2367cc2493dSspupyrev Now = Pred; 2377cc2493dSspupyrev } 2387cc2493dSspupyrev } 2397cc2493dSspupyrev 240*13d1364aSspupyrev /// A node in a flow network. 2417cc2493dSspupyrev struct Node { 2427cc2493dSspupyrev /// The cost of the cheapest path from the source to the current node. 2437cc2493dSspupyrev int64_t Distance; 2447cc2493dSspupyrev /// The node preceding the current one in the path. 2457cc2493dSspupyrev uint64_t ParentNode; 2467cc2493dSspupyrev /// The index of the edge between ParentNode and the current node. 2477cc2493dSspupyrev uint64_t ParentEdgeIndex; 2487cc2493dSspupyrev /// An indicator of whether the current node is in a queue. 2497cc2493dSspupyrev bool Taken; 2507cc2493dSspupyrev }; 2517cc2493dSspupyrev /// An edge in a flow network. 2527cc2493dSspupyrev struct Edge { 2537cc2493dSspupyrev /// The cost of the edge. 2547cc2493dSspupyrev int64_t Cost; 2557cc2493dSspupyrev /// The capacity of the edge. 2567cc2493dSspupyrev int64_t Capacity; 2577cc2493dSspupyrev /// The current flow on the edge. 2587cc2493dSspupyrev int64_t Flow; 2597cc2493dSspupyrev /// The destination node of the edge. 2607cc2493dSspupyrev uint64_t Dst; 2617cc2493dSspupyrev /// The index of the reverse edge between Dst and the current node. 2627cc2493dSspupyrev uint64_t RevEdgeIndex; 2637cc2493dSspupyrev }; 2647cc2493dSspupyrev 2657cc2493dSspupyrev /// The set of network nodes. 2667cc2493dSspupyrev std::vector<Node> Nodes; 2677cc2493dSspupyrev /// The set of network edges. 2687cc2493dSspupyrev std::vector<std::vector<Edge>> Edges; 2697cc2493dSspupyrev /// Source node of the flow. 2707cc2493dSspupyrev uint64_t Source; 2717cc2493dSspupyrev /// Target (sink) node of the flow. 2727cc2493dSspupyrev uint64_t Target; 2737cc2493dSspupyrev }; 2747cc2493dSspupyrev 27593a2c291Sspupyrev /// A post-processing adjustment of control flow. It applies two steps by 27693a2c291Sspupyrev /// rerouting some flow and making it more realistic: 27793a2c291Sspupyrev /// 27893a2c291Sspupyrev /// - First, it removes all isolated components ("islands") with a positive flow 27993a2c291Sspupyrev /// that are unreachable from the entry block. For every such component, we 28093a2c291Sspupyrev /// find the shortest from the entry to an exit passing through the component, 28193a2c291Sspupyrev /// and increase the flow by one unit along the path. 28293a2c291Sspupyrev /// 28393a2c291Sspupyrev /// - Second, it identifies all "unknown subgraphs" consisting of basic blocks 28493a2c291Sspupyrev /// with no sampled counts. Then it rebalnces the flow that goes through such 28593a2c291Sspupyrev /// a subgraph so that each branch is taken with probability 50%. 28693a2c291Sspupyrev /// An unknown subgraph is such that for every two nodes u and v: 28793a2c291Sspupyrev /// - u dominates v and u is not unknown; 28893a2c291Sspupyrev /// - v post-dominates u; and 28993a2c291Sspupyrev /// - all inner-nodes of all (u,v)-paths are unknown. 29093a2c291Sspupyrev /// 29198dd2f9eSspupyrev class FlowAdjuster { 29298dd2f9eSspupyrev public: 29398dd2f9eSspupyrev FlowAdjuster(FlowFunction &Func) : Func(Func) { 29498dd2f9eSspupyrev assert(Func.Blocks[Func.Entry].isEntry() && 29598dd2f9eSspupyrev "incorrect index of the entry block"); 29698dd2f9eSspupyrev } 29798dd2f9eSspupyrev 29898dd2f9eSspupyrev // Run the post-processing 29998dd2f9eSspupyrev void run() { 30093a2c291Sspupyrev /// Adjust the flow to get rid of isolated components. 30198dd2f9eSspupyrev joinIsolatedComponents(); 30293a2c291Sspupyrev 30393a2c291Sspupyrev /// Rebalance the flow inside unknown subgraphs. 30493a2c291Sspupyrev rebalanceUnknownSubgraphs(); 30598dd2f9eSspupyrev } 30698dd2f9eSspupyrev 30798dd2f9eSspupyrev private: 30898dd2f9eSspupyrev void joinIsolatedComponents() { 30998dd2f9eSspupyrev // Find blocks that are reachable from the source 3105f4ae564SJan Svoboda auto Visited = BitVector(NumBlocks(), false); 31198dd2f9eSspupyrev findReachable(Func.Entry, Visited); 31298dd2f9eSspupyrev 31398dd2f9eSspupyrev // Iterate over all non-reachable blocks and adjust their weights 31498dd2f9eSspupyrev for (uint64_t I = 0; I < NumBlocks(); I++) { 31598dd2f9eSspupyrev auto &Block = Func.Blocks[I]; 31698dd2f9eSspupyrev if (Block.Flow > 0 && !Visited[I]) { 31798dd2f9eSspupyrev // Find a path from the entry to an exit passing through the block I 31898dd2f9eSspupyrev auto Path = findShortestPath(I); 31998dd2f9eSspupyrev // Increase the flow along the path 32098dd2f9eSspupyrev assert(Path.size() > 0 && Path[0]->Source == Func.Entry && 32198dd2f9eSspupyrev "incorrectly computed path adjusting control flow"); 32298dd2f9eSspupyrev Func.Blocks[Func.Entry].Flow += 1; 32398dd2f9eSspupyrev for (auto &Jump : Path) { 32498dd2f9eSspupyrev Jump->Flow += 1; 32598dd2f9eSspupyrev Func.Blocks[Jump->Target].Flow += 1; 32698dd2f9eSspupyrev // Update reachability 32798dd2f9eSspupyrev findReachable(Jump->Target, Visited); 32898dd2f9eSspupyrev } 32998dd2f9eSspupyrev } 33098dd2f9eSspupyrev } 33198dd2f9eSspupyrev } 33298dd2f9eSspupyrev 33393a2c291Sspupyrev /// Run BFS from a given block along the jumps with a positive flow and mark 33498dd2f9eSspupyrev /// all reachable blocks. 3355f4ae564SJan Svoboda void findReachable(uint64_t Src, BitVector &Visited) { 33698dd2f9eSspupyrev if (Visited[Src]) 33798dd2f9eSspupyrev return; 33898dd2f9eSspupyrev std::queue<uint64_t> Queue; 33998dd2f9eSspupyrev Queue.push(Src); 34098dd2f9eSspupyrev Visited[Src] = true; 34198dd2f9eSspupyrev while (!Queue.empty()) { 34298dd2f9eSspupyrev Src = Queue.front(); 34398dd2f9eSspupyrev Queue.pop(); 34498dd2f9eSspupyrev for (auto Jump : Func.Blocks[Src].SuccJumps) { 34598dd2f9eSspupyrev uint64_t Dst = Jump->Target; 34698dd2f9eSspupyrev if (Jump->Flow > 0 && !Visited[Dst]) { 34798dd2f9eSspupyrev Queue.push(Dst); 34898dd2f9eSspupyrev Visited[Dst] = true; 34998dd2f9eSspupyrev } 35098dd2f9eSspupyrev } 35198dd2f9eSspupyrev } 35298dd2f9eSspupyrev } 35398dd2f9eSspupyrev 35498dd2f9eSspupyrev /// Find the shortest path from the entry block to an exit block passing 35598dd2f9eSspupyrev /// through a given block. 35698dd2f9eSspupyrev std::vector<FlowJump *> findShortestPath(uint64_t BlockIdx) { 35798dd2f9eSspupyrev // A path from the entry block to BlockIdx 35898dd2f9eSspupyrev auto ForwardPath = findShortestPath(Func.Entry, BlockIdx); 35998dd2f9eSspupyrev // A path from BlockIdx to an exit block 36098dd2f9eSspupyrev auto BackwardPath = findShortestPath(BlockIdx, AnyExitBlock); 36198dd2f9eSspupyrev 36298dd2f9eSspupyrev // Concatenate the two paths 36398dd2f9eSspupyrev std::vector<FlowJump *> Result; 36498dd2f9eSspupyrev Result.insert(Result.end(), ForwardPath.begin(), ForwardPath.end()); 36598dd2f9eSspupyrev Result.insert(Result.end(), BackwardPath.begin(), BackwardPath.end()); 36698dd2f9eSspupyrev return Result; 36798dd2f9eSspupyrev } 36898dd2f9eSspupyrev 36998dd2f9eSspupyrev /// Apply the Dijkstra algorithm to find the shortest path from a given 37098dd2f9eSspupyrev /// Source to a given Target block. 37198dd2f9eSspupyrev /// If Target == -1, then the path ends at an exit block. 37298dd2f9eSspupyrev std::vector<FlowJump *> findShortestPath(uint64_t Source, uint64_t Target) { 37398dd2f9eSspupyrev // Quit early, if possible 37498dd2f9eSspupyrev if (Source == Target) 37598dd2f9eSspupyrev return std::vector<FlowJump *>(); 37698dd2f9eSspupyrev if (Func.Blocks[Source].isExit() && Target == AnyExitBlock) 37798dd2f9eSspupyrev return std::vector<FlowJump *>(); 37898dd2f9eSspupyrev 37998dd2f9eSspupyrev // Initialize data structures 38098dd2f9eSspupyrev auto Distance = std::vector<int64_t>(NumBlocks(), INF); 38198dd2f9eSspupyrev auto Parent = std::vector<FlowJump *>(NumBlocks(), nullptr); 38298dd2f9eSspupyrev Distance[Source] = 0; 38398dd2f9eSspupyrev std::set<std::pair<uint64_t, uint64_t>> Queue; 38498dd2f9eSspupyrev Queue.insert(std::make_pair(Distance[Source], Source)); 38598dd2f9eSspupyrev 38698dd2f9eSspupyrev // Run the Dijkstra algorithm 38798dd2f9eSspupyrev while (!Queue.empty()) { 38898dd2f9eSspupyrev uint64_t Src = Queue.begin()->second; 38998dd2f9eSspupyrev Queue.erase(Queue.begin()); 39098dd2f9eSspupyrev // If we found a solution, quit early 39198dd2f9eSspupyrev if (Src == Target || 39298dd2f9eSspupyrev (Func.Blocks[Src].isExit() && Target == AnyExitBlock)) 39398dd2f9eSspupyrev break; 39498dd2f9eSspupyrev 39598dd2f9eSspupyrev for (auto Jump : Func.Blocks[Src].SuccJumps) { 39698dd2f9eSspupyrev uint64_t Dst = Jump->Target; 39798dd2f9eSspupyrev int64_t JumpDist = jumpDistance(Jump); 39898dd2f9eSspupyrev if (Distance[Dst] > Distance[Src] + JumpDist) { 39998dd2f9eSspupyrev Queue.erase(std::make_pair(Distance[Dst], Dst)); 40098dd2f9eSspupyrev 40198dd2f9eSspupyrev Distance[Dst] = Distance[Src] + JumpDist; 40298dd2f9eSspupyrev Parent[Dst] = Jump; 40398dd2f9eSspupyrev 40498dd2f9eSspupyrev Queue.insert(std::make_pair(Distance[Dst], Dst)); 40598dd2f9eSspupyrev } 40698dd2f9eSspupyrev } 40798dd2f9eSspupyrev } 40898dd2f9eSspupyrev // If Target is not provided, find the closest exit block 40998dd2f9eSspupyrev if (Target == AnyExitBlock) { 41098dd2f9eSspupyrev for (uint64_t I = 0; I < NumBlocks(); I++) { 41198dd2f9eSspupyrev if (Func.Blocks[I].isExit() && Parent[I] != nullptr) { 41298dd2f9eSspupyrev if (Target == AnyExitBlock || Distance[Target] > Distance[I]) { 41398dd2f9eSspupyrev Target = I; 41498dd2f9eSspupyrev } 41598dd2f9eSspupyrev } 41698dd2f9eSspupyrev } 41798dd2f9eSspupyrev } 41898dd2f9eSspupyrev assert(Parent[Target] != nullptr && "a path does not exist"); 41998dd2f9eSspupyrev 42098dd2f9eSspupyrev // Extract the constructed path 42198dd2f9eSspupyrev std::vector<FlowJump *> Result; 42298dd2f9eSspupyrev uint64_t Now = Target; 42398dd2f9eSspupyrev while (Now != Source) { 42498dd2f9eSspupyrev assert(Now == Parent[Now]->Target && "incorrect parent jump"); 42598dd2f9eSspupyrev Result.push_back(Parent[Now]); 42698dd2f9eSspupyrev Now = Parent[Now]->Source; 42798dd2f9eSspupyrev } 42898dd2f9eSspupyrev // Reverse the path, since it is extracted from Target to Source 42998dd2f9eSspupyrev std::reverse(Result.begin(), Result.end()); 43098dd2f9eSspupyrev return Result; 43198dd2f9eSspupyrev } 43298dd2f9eSspupyrev 43398dd2f9eSspupyrev /// A distance of a path for a given jump. 43498dd2f9eSspupyrev /// In order to incite the path to use blocks/jumps with large positive flow, 43598dd2f9eSspupyrev /// and avoid changing branch probability of outgoing edges drastically, 43698dd2f9eSspupyrev /// set the distance as follows: 43798dd2f9eSspupyrev /// if Jump.Flow > 0, then distance = max(100 - Jump->Flow, 0) 43898dd2f9eSspupyrev /// if Block.Weight > 0, then distance = 1 43998dd2f9eSspupyrev /// otherwise distance >> 1 44098dd2f9eSspupyrev int64_t jumpDistance(FlowJump *Jump) const { 44198dd2f9eSspupyrev int64_t BaseDistance = 100; 44298dd2f9eSspupyrev if (Jump->IsUnlikely) 44398dd2f9eSspupyrev return MinCostMaxFlow::AuxCostUnlikely; 44498dd2f9eSspupyrev if (Jump->Flow > 0) 44598dd2f9eSspupyrev return std::max(BaseDistance - (int64_t)Jump->Flow, (int64_t)0); 44698dd2f9eSspupyrev if (Func.Blocks[Jump->Target].Weight > 0) 44798dd2f9eSspupyrev return BaseDistance; 44898dd2f9eSspupyrev return BaseDistance * (NumBlocks() + 1); 44998dd2f9eSspupyrev }; 45098dd2f9eSspupyrev 45198dd2f9eSspupyrev uint64_t NumBlocks() const { return Func.Blocks.size(); } 45298dd2f9eSspupyrev 453*13d1364aSspupyrev /// Rebalance unknown subgraphs so that the flow is split evenly across the 454*13d1364aSspupyrev /// outgoing branches of every block of the subgraph. The method iterates over 455*13d1364aSspupyrev /// blocks with known weight and identifies unknown subgraphs rooted at the 456*13d1364aSspupyrev /// blocks. Then it verifies if flow rebalancing is feasible and applies it. 45793a2c291Sspupyrev void rebalanceUnknownSubgraphs() { 458*13d1364aSspupyrev // Try to find unknown subgraphs from each block 45993a2c291Sspupyrev for (uint64_t I = 0; I < Func.Blocks.size(); I++) { 46093a2c291Sspupyrev auto SrcBlock = &Func.Blocks[I]; 461*13d1364aSspupyrev // Verify if rebalancing rooted at SrcBlock is feasible 462*13d1364aSspupyrev if (!canRebalanceAtRoot(SrcBlock)) 46393a2c291Sspupyrev continue; 46493a2c291Sspupyrev 465*13d1364aSspupyrev // Find an unknown subgraphs starting at SrcBlock. Along the way, 466*13d1364aSspupyrev // fill in known destinations and intermediate unknown blocks. 467*13d1364aSspupyrev std::vector<FlowBlock *> UnknownBlocks; 468*13d1364aSspupyrev std::vector<FlowBlock *> KnownDstBlocks; 469*13d1364aSspupyrev findUnknownSubgraph(SrcBlock, KnownDstBlocks, UnknownBlocks); 470*13d1364aSspupyrev 471*13d1364aSspupyrev // Verify if rebalancing of the subgraph is feasible. If the search is 472*13d1364aSspupyrev // successful, find the unique destination block (which can be null) 47393a2c291Sspupyrev FlowBlock *DstBlock = nullptr; 474*13d1364aSspupyrev if (!canRebalanceSubgraph(SrcBlock, KnownDstBlocks, UnknownBlocks, 475*13d1364aSspupyrev DstBlock)) 47693a2c291Sspupyrev continue; 477*13d1364aSspupyrev 478*13d1364aSspupyrev // We cannot rebalance subgraphs containing cycles among unknown blocks 479*13d1364aSspupyrev if (!isAcyclicSubgraph(SrcBlock, DstBlock, UnknownBlocks)) 48093a2c291Sspupyrev continue; 48193a2c291Sspupyrev 48293a2c291Sspupyrev // Rebalance the flow 483*13d1364aSspupyrev rebalanceUnknownSubgraph(SrcBlock, DstBlock, UnknownBlocks); 48493a2c291Sspupyrev } 48593a2c291Sspupyrev } 48693a2c291Sspupyrev 487*13d1364aSspupyrev /// Verify if rebalancing rooted at a given block is possible. 488*13d1364aSspupyrev bool canRebalanceAtRoot(const FlowBlock *SrcBlock) { 489*13d1364aSspupyrev // Do not attempt to find unknown subgraphs from an unknown or a 490*13d1364aSspupyrev // zero-flow block 491*13d1364aSspupyrev if (SrcBlock->UnknownWeight || SrcBlock->Flow == 0) 492*13d1364aSspupyrev return false; 493*13d1364aSspupyrev 494*13d1364aSspupyrev // Do not attempt to process subgraphs from a block w/o unknown sucessors 495*13d1364aSspupyrev bool HasUnknownSuccs = false; 496*13d1364aSspupyrev for (auto Jump : SrcBlock->SuccJumps) { 497*13d1364aSspupyrev if (Func.Blocks[Jump->Target].UnknownWeight) { 498*13d1364aSspupyrev HasUnknownSuccs = true; 499*13d1364aSspupyrev break; 500*13d1364aSspupyrev } 501*13d1364aSspupyrev } 502*13d1364aSspupyrev if (!HasUnknownSuccs) 503*13d1364aSspupyrev return false; 504*13d1364aSspupyrev 505*13d1364aSspupyrev return true; 506*13d1364aSspupyrev } 507*13d1364aSspupyrev 508*13d1364aSspupyrev /// Find an unknown subgraph starting at block SrcBlock. The method sets 509*13d1364aSspupyrev /// identified destinations, KnownDstBlocks, and intermediate UnknownBlocks. 510*13d1364aSspupyrev void findUnknownSubgraph(const FlowBlock *SrcBlock, 511*13d1364aSspupyrev std::vector<FlowBlock *> &KnownDstBlocks, 512*13d1364aSspupyrev std::vector<FlowBlock *> &UnknownBlocks) { 51393a2c291Sspupyrev // Run BFS from SrcBlock and make sure all paths are going through unknown 51493a2c291Sspupyrev // blocks and end at a non-unknown DstBlock 5155f4ae564SJan Svoboda auto Visited = BitVector(NumBlocks(), false); 51693a2c291Sspupyrev std::queue<uint64_t> Queue; 51793a2c291Sspupyrev 51893a2c291Sspupyrev Queue.push(SrcBlock->Index); 51993a2c291Sspupyrev Visited[SrcBlock->Index] = true; 52093a2c291Sspupyrev while (!Queue.empty()) { 52193a2c291Sspupyrev auto &Block = Func.Blocks[Queue.front()]; 52293a2c291Sspupyrev Queue.pop(); 52393a2c291Sspupyrev // Process blocks reachable from Block 52493a2c291Sspupyrev for (auto Jump : Block.SuccJumps) { 525*13d1364aSspupyrev // If Jump can be ignored, skip it 526*13d1364aSspupyrev if (ignoreJump(SrcBlock, nullptr, Jump)) 527*13d1364aSspupyrev continue; 528*13d1364aSspupyrev 52993a2c291Sspupyrev uint64_t Dst = Jump->Target; 530*13d1364aSspupyrev // If Dst has been visited, skip Jump 53193a2c291Sspupyrev if (Visited[Dst]) 53293a2c291Sspupyrev continue; 533*13d1364aSspupyrev // Process block Dst 53493a2c291Sspupyrev Visited[Dst] = true; 53593a2c291Sspupyrev if (!Func.Blocks[Dst].UnknownWeight) { 536*13d1364aSspupyrev KnownDstBlocks.push_back(&Func.Blocks[Dst]); 53793a2c291Sspupyrev } else { 53893a2c291Sspupyrev Queue.push(Dst); 539*13d1364aSspupyrev UnknownBlocks.push_back(&Func.Blocks[Dst]); 540*13d1364aSspupyrev } 54193a2c291Sspupyrev } 54293a2c291Sspupyrev } 54393a2c291Sspupyrev } 54493a2c291Sspupyrev 545*13d1364aSspupyrev /// Verify if rebalancing of the subgraph is feasible. If the checks are 546*13d1364aSspupyrev /// successful, set the unique destination block, DstBlock (can be null). 547*13d1364aSspupyrev bool canRebalanceSubgraph(const FlowBlock *SrcBlock, 548*13d1364aSspupyrev const std::vector<FlowBlock *> &KnownDstBlocks, 549*13d1364aSspupyrev const std::vector<FlowBlock *> &UnknownBlocks, 550*13d1364aSspupyrev FlowBlock *&DstBlock) { 55193a2c291Sspupyrev // If the list of unknown blocks is empty, we don't need rebalancing 552*13d1364aSspupyrev if (UnknownBlocks.empty()) 55393a2c291Sspupyrev return false; 554*13d1364aSspupyrev 555*13d1364aSspupyrev // If there are multiple known sinks, we can't rebalance 556*13d1364aSspupyrev if (KnownDstBlocks.size() > 1) 55793a2c291Sspupyrev return false; 558*13d1364aSspupyrev DstBlock = KnownDstBlocks.empty() ? nullptr : KnownDstBlocks.front(); 559*13d1364aSspupyrev 560*13d1364aSspupyrev // Verify sinks of the subgraph 561*13d1364aSspupyrev for (auto Block : UnknownBlocks) { 562*13d1364aSspupyrev if (Block->SuccJumps.empty()) { 563*13d1364aSspupyrev // If there are multiple (known and unknown) sinks, we can't rebalance 564*13d1364aSspupyrev if (DstBlock != nullptr) 565*13d1364aSspupyrev return false; 566*13d1364aSspupyrev continue; 567*13d1364aSspupyrev } 568*13d1364aSspupyrev size_t NumIgnoredJumps = 0; 569*13d1364aSspupyrev for (auto Jump : Block->SuccJumps) { 570*13d1364aSspupyrev if (ignoreJump(SrcBlock, DstBlock, Jump)) 571*13d1364aSspupyrev NumIgnoredJumps++; 572*13d1364aSspupyrev } 573*13d1364aSspupyrev // If there is a non-sink block in UnknownBlocks with all jumps ignored, 574*13d1364aSspupyrev // then we can't rebalance 575*13d1364aSspupyrev if (NumIgnoredJumps == Block->SuccJumps.size()) 57693a2c291Sspupyrev return false; 57793a2c291Sspupyrev } 57893a2c291Sspupyrev 57993a2c291Sspupyrev return true; 58093a2c291Sspupyrev } 58193a2c291Sspupyrev 582*13d1364aSspupyrev /// Decide whether the Jump is ignored while processing an unknown subgraphs 583*13d1364aSspupyrev /// rooted at basic block SrcBlock with the destination block, DstBlock. 584*13d1364aSspupyrev bool ignoreJump(const FlowBlock *SrcBlock, const FlowBlock *DstBlock, 585*13d1364aSspupyrev const FlowJump *Jump) { 586*13d1364aSspupyrev // Ignore unlikely jumps with zero flow 587*13d1364aSspupyrev if (Jump->IsUnlikely && Jump->Flow == 0) 588*13d1364aSspupyrev return true; 589*13d1364aSspupyrev 590*13d1364aSspupyrev auto JumpSource = &Func.Blocks[Jump->Source]; 591*13d1364aSspupyrev auto JumpTarget = &Func.Blocks[Jump->Target]; 592*13d1364aSspupyrev 593*13d1364aSspupyrev // Do not ignore jumps coming into DstBlock 594*13d1364aSspupyrev if (DstBlock != nullptr && JumpTarget == DstBlock) 595*13d1364aSspupyrev return false; 596*13d1364aSspupyrev 597*13d1364aSspupyrev // Ignore jumps out of SrcBlock to known blocks 598*13d1364aSspupyrev if (!JumpTarget->UnknownWeight && JumpSource == SrcBlock) 599*13d1364aSspupyrev return true; 600*13d1364aSspupyrev 601*13d1364aSspupyrev // Ignore jumps to known blocks with zero flow 602*13d1364aSspupyrev if (!JumpTarget->UnknownWeight && JumpTarget->Flow == 0) 603*13d1364aSspupyrev return true; 604*13d1364aSspupyrev 605*13d1364aSspupyrev return false; 606*13d1364aSspupyrev } 607*13d1364aSspupyrev 60893a2c291Sspupyrev /// Verify if the given unknown subgraph is acyclic, and if yes, reorder 609*13d1364aSspupyrev /// UnknownBlocks in the topological order (so that all jumps are "forward"). 610*13d1364aSspupyrev bool isAcyclicSubgraph(const FlowBlock *SrcBlock, const FlowBlock *DstBlock, 611*13d1364aSspupyrev std::vector<FlowBlock *> &UnknownBlocks) { 61293a2c291Sspupyrev // Extract local in-degrees in the considered subgraph 61393a2c291Sspupyrev auto LocalInDegree = std::vector<uint64_t>(NumBlocks(), 0); 614*13d1364aSspupyrev auto fillInDegree = [&](const FlowBlock *Block) { 615*13d1364aSspupyrev for (auto Jump : Block->SuccJumps) { 616*13d1364aSspupyrev if (ignoreJump(SrcBlock, DstBlock, Jump)) 617*13d1364aSspupyrev continue; 61893a2c291Sspupyrev LocalInDegree[Jump->Target]++; 61993a2c291Sspupyrev } 620*13d1364aSspupyrev }; 621*13d1364aSspupyrev fillInDegree(SrcBlock); 622*13d1364aSspupyrev for (auto Block : UnknownBlocks) { 623*13d1364aSspupyrev fillInDegree(Block); 62493a2c291Sspupyrev } 62593a2c291Sspupyrev // A loop containing SrcBlock 62693a2c291Sspupyrev if (LocalInDegree[SrcBlock->Index] > 0) 62793a2c291Sspupyrev return false; 62893a2c291Sspupyrev 62993a2c291Sspupyrev std::vector<FlowBlock *> AcyclicOrder; 63093a2c291Sspupyrev std::queue<uint64_t> Queue; 63193a2c291Sspupyrev Queue.push(SrcBlock->Index); 63293a2c291Sspupyrev while (!Queue.empty()) { 633*13d1364aSspupyrev FlowBlock *Block = &Func.Blocks[Queue.front()]; 63493a2c291Sspupyrev Queue.pop(); 635*13d1364aSspupyrev // Stop propagation once we reach DstBlock, if any 636*13d1364aSspupyrev if (DstBlock != nullptr && Block == DstBlock) 63793a2c291Sspupyrev break; 63893a2c291Sspupyrev 639*13d1364aSspupyrev // Keep an acyclic order of unknown blocks 640*13d1364aSspupyrev if (Block->UnknownWeight && Block != SrcBlock) 641*13d1364aSspupyrev AcyclicOrder.push_back(Block); 642*13d1364aSspupyrev 64393a2c291Sspupyrev // Add to the queue all successors with zero local in-degree 644*13d1364aSspupyrev for (auto Jump : Block->SuccJumps) { 645*13d1364aSspupyrev if (ignoreJump(SrcBlock, DstBlock, Jump)) 646*13d1364aSspupyrev continue; 64793a2c291Sspupyrev uint64_t Dst = Jump->Target; 64893a2c291Sspupyrev LocalInDegree[Dst]--; 64993a2c291Sspupyrev if (LocalInDegree[Dst] == 0) { 65093a2c291Sspupyrev Queue.push(Dst); 65193a2c291Sspupyrev } 65293a2c291Sspupyrev } 65393a2c291Sspupyrev } 65493a2c291Sspupyrev 65593a2c291Sspupyrev // If there is a cycle in the subgraph, AcyclicOrder contains only a subset 65693a2c291Sspupyrev // of all blocks 657*13d1364aSspupyrev if (UnknownBlocks.size() != AcyclicOrder.size()) 65893a2c291Sspupyrev return false; 659*13d1364aSspupyrev UnknownBlocks = AcyclicOrder; 66093a2c291Sspupyrev return true; 66193a2c291Sspupyrev } 66293a2c291Sspupyrev 663*13d1364aSspupyrev /// Rebalance a given subgraph rooted at SrcBlock, ending at DstBlock and 664*13d1364aSspupyrev /// having UnknownBlocks intermediate blocks. 665*13d1364aSspupyrev void rebalanceUnknownSubgraph(const FlowBlock *SrcBlock, 666*13d1364aSspupyrev const FlowBlock *DstBlock, 667*13d1364aSspupyrev const std::vector<FlowBlock *> &UnknownBlocks) { 66893a2c291Sspupyrev assert(SrcBlock->Flow > 0 && "zero-flow block in unknown subgraph"); 66993a2c291Sspupyrev 670*13d1364aSspupyrev // Ditribute flow from the source block 671*13d1364aSspupyrev uint64_t BlockFlow = 0; 672*13d1364aSspupyrev // SrcBlock's flow is the sum of outgoing flows along non-ignored jumps 673*13d1364aSspupyrev for (auto Jump : SrcBlock->SuccJumps) { 674*13d1364aSspupyrev if (ignoreJump(SrcBlock, DstBlock, Jump)) 67593a2c291Sspupyrev continue; 676*13d1364aSspupyrev BlockFlow += Jump->Flow; 67793a2c291Sspupyrev } 678*13d1364aSspupyrev rebalanceBlock(SrcBlock, DstBlock, SrcBlock, BlockFlow); 679*13d1364aSspupyrev 680*13d1364aSspupyrev // Ditribute flow from the remaining blocks 681*13d1364aSspupyrev for (auto Block : UnknownBlocks) { 682*13d1364aSspupyrev assert(Block->UnknownWeight && "incorrect unknown subgraph"); 683*13d1364aSspupyrev uint64_t BlockFlow = 0; 684*13d1364aSspupyrev // Block's flow is the sum of incoming flows 685*13d1364aSspupyrev for (auto Jump : Block->PredJumps) { 686*13d1364aSspupyrev BlockFlow += Jump->Flow; 687*13d1364aSspupyrev } 688*13d1364aSspupyrev Block->Flow = BlockFlow; 689*13d1364aSspupyrev rebalanceBlock(SrcBlock, DstBlock, Block, BlockFlow); 690*13d1364aSspupyrev } 691*13d1364aSspupyrev } 692*13d1364aSspupyrev 693*13d1364aSspupyrev /// Redistribute flow for a block in a subgraph rooted at SrcBlock, 694*13d1364aSspupyrev /// and ending at DstBlock. 695*13d1364aSspupyrev void rebalanceBlock(const FlowBlock *SrcBlock, const FlowBlock *DstBlock, 696*13d1364aSspupyrev const FlowBlock *Block, uint64_t BlockFlow) { 697*13d1364aSspupyrev // Process all successor jumps and update corresponding flow values 698*13d1364aSspupyrev size_t BlockDegree = 0; 699*13d1364aSspupyrev for (auto Jump : Block->SuccJumps) { 700*13d1364aSspupyrev if (ignoreJump(SrcBlock, DstBlock, Jump)) 701*13d1364aSspupyrev continue; 702*13d1364aSspupyrev BlockDegree++; 703*13d1364aSspupyrev } 704*13d1364aSspupyrev // If all successor jumps of the block are ignored, skip it 705*13d1364aSspupyrev if (DstBlock == nullptr && BlockDegree == 0) 706*13d1364aSspupyrev return; 707*13d1364aSspupyrev assert(BlockDegree > 0 && "all outgoing jumps are ignored"); 708*13d1364aSspupyrev 709*13d1364aSspupyrev // Each of the Block's successors gets the following amount of flow. 710*13d1364aSspupyrev // Rounding the value up so that all flow is propagated 711*13d1364aSspupyrev uint64_t SuccFlow = (BlockFlow + BlockDegree - 1) / BlockDegree; 712*13d1364aSspupyrev for (auto Jump : Block->SuccJumps) { 713*13d1364aSspupyrev if (ignoreJump(SrcBlock, DstBlock, Jump)) 714*13d1364aSspupyrev continue; 715*13d1364aSspupyrev uint64_t Flow = std::min(SuccFlow, BlockFlow); 71693a2c291Sspupyrev Jump->Flow = Flow; 717*13d1364aSspupyrev BlockFlow -= Flow; 71893a2c291Sspupyrev } 719*13d1364aSspupyrev assert(BlockFlow == 0 && "not all flow is propagated"); 72093a2c291Sspupyrev } 72193a2c291Sspupyrev 72298dd2f9eSspupyrev /// A constant indicating an arbitrary exit block of a function. 72398dd2f9eSspupyrev static constexpr uint64_t AnyExitBlock = uint64_t(-1); 72498dd2f9eSspupyrev 72598dd2f9eSspupyrev /// The function. 72698dd2f9eSspupyrev FlowFunction &Func; 72798dd2f9eSspupyrev }; 72898dd2f9eSspupyrev 7297cc2493dSspupyrev /// Initializing flow network for a given function. 7307cc2493dSspupyrev /// 7317cc2493dSspupyrev /// Every block is split into three nodes that are responsible for (i) an 7327cc2493dSspupyrev /// incoming flow, (ii) an outgoing flow, and (iii) penalizing an increase or 7337cc2493dSspupyrev /// reduction of the block weight. 7347cc2493dSspupyrev void initializeNetwork(MinCostMaxFlow &Network, FlowFunction &Func) { 7357cc2493dSspupyrev uint64_t NumBlocks = Func.Blocks.size(); 7367cc2493dSspupyrev assert(NumBlocks > 1 && "Too few blocks in a function"); 7377cc2493dSspupyrev LLVM_DEBUG(dbgs() << "Initializing profi for " << NumBlocks << " blocks\n"); 7387cc2493dSspupyrev 7397cc2493dSspupyrev // Pre-process data: make sure the entry weight is at least 1 7407cc2493dSspupyrev if (Func.Blocks[Func.Entry].Weight == 0) { 7417cc2493dSspupyrev Func.Blocks[Func.Entry].Weight = 1; 7427cc2493dSspupyrev } 7437cc2493dSspupyrev // Introducing dummy source/sink pairs to allow flow circulation. 7447cc2493dSspupyrev // The nodes corresponding to blocks of Func have indicies in the range 7457cc2493dSspupyrev // [0..3 * NumBlocks); the dummy nodes are indexed by the next four values. 7467cc2493dSspupyrev uint64_t S = 3 * NumBlocks; 7477cc2493dSspupyrev uint64_t T = S + 1; 7487cc2493dSspupyrev uint64_t S1 = S + 2; 7497cc2493dSspupyrev uint64_t T1 = S + 3; 7507cc2493dSspupyrev 7517cc2493dSspupyrev Network.initialize(3 * NumBlocks + 4, S1, T1); 7527cc2493dSspupyrev 7537cc2493dSspupyrev // Create three nodes for every block of the function 7547cc2493dSspupyrev for (uint64_t B = 0; B < NumBlocks; B++) { 7557cc2493dSspupyrev auto &Block = Func.Blocks[B]; 7567cc2493dSspupyrev assert((!Block.UnknownWeight || Block.Weight == 0 || Block.isEntry()) && 7577cc2493dSspupyrev "non-zero weight of a block w/o weight except for an entry"); 7587cc2493dSspupyrev 7597cc2493dSspupyrev // Split every block into two nodes 7607cc2493dSspupyrev uint64_t Bin = 3 * B; 7617cc2493dSspupyrev uint64_t Bout = 3 * B + 1; 7627cc2493dSspupyrev uint64_t Baux = 3 * B + 2; 7637cc2493dSspupyrev if (Block.Weight > 0) { 7647cc2493dSspupyrev Network.addEdge(S1, Bout, Block.Weight, 0); 7657cc2493dSspupyrev Network.addEdge(Bin, T1, Block.Weight, 0); 7667cc2493dSspupyrev } 7677cc2493dSspupyrev 7687cc2493dSspupyrev // Edges from S and to T 7697cc2493dSspupyrev assert((!Block.isEntry() || !Block.isExit()) && 7707cc2493dSspupyrev "a block cannot be an entry and an exit"); 7717cc2493dSspupyrev if (Block.isEntry()) { 7727cc2493dSspupyrev Network.addEdge(S, Bin, 0); 7737cc2493dSspupyrev } else if (Block.isExit()) { 7747cc2493dSspupyrev Network.addEdge(Bout, T, 0); 7757cc2493dSspupyrev } 7767cc2493dSspupyrev 7777cc2493dSspupyrev // An auxiliary node to allow increase/reduction of block counts: 7787cc2493dSspupyrev // We assume that decreasing block counts is more expensive than increasing, 7797cc2493dSspupyrev // and thus, setting separate costs here. In the future we may want to tune 7807cc2493dSspupyrev // the relative costs so as to maximize the quality of generated profiles. 7817cc2493dSspupyrev int64_t AuxCostInc = MinCostMaxFlow::AuxCostInc; 7827cc2493dSspupyrev int64_t AuxCostDec = MinCostMaxFlow::AuxCostDec; 7837cc2493dSspupyrev if (Block.UnknownWeight) { 7847cc2493dSspupyrev // Do not penalize changing weights of blocks w/o known profile count 7857cc2493dSspupyrev AuxCostInc = 0; 7867cc2493dSspupyrev AuxCostDec = 0; 7877cc2493dSspupyrev } else { 7887cc2493dSspupyrev // Increasing the count for "cold" blocks with zero initial count is more 7897cc2493dSspupyrev // expensive than for "hot" ones 7907cc2493dSspupyrev if (Block.Weight == 0) { 7917cc2493dSspupyrev AuxCostInc = MinCostMaxFlow::AuxCostIncZero; 7927cc2493dSspupyrev } 7937cc2493dSspupyrev // Modifying the count of the entry block is expensive 7947cc2493dSspupyrev if (Block.isEntry()) { 7957cc2493dSspupyrev AuxCostInc = MinCostMaxFlow::AuxCostIncEntry; 7967cc2493dSspupyrev AuxCostDec = MinCostMaxFlow::AuxCostDecEntry; 7977cc2493dSspupyrev } 7987cc2493dSspupyrev } 7997cc2493dSspupyrev // For blocks with self-edges, do not penalize a reduction of the count, 8007cc2493dSspupyrev // as all of the increase can be attributed to the self-edge 8017cc2493dSspupyrev if (Block.HasSelfEdge) { 8027cc2493dSspupyrev AuxCostDec = 0; 8037cc2493dSspupyrev } 8047cc2493dSspupyrev 8057cc2493dSspupyrev Network.addEdge(Bin, Baux, AuxCostInc); 8067cc2493dSspupyrev Network.addEdge(Baux, Bout, AuxCostInc); 8077cc2493dSspupyrev if (Block.Weight > 0) { 8087cc2493dSspupyrev Network.addEdge(Bout, Baux, AuxCostDec); 8097cc2493dSspupyrev Network.addEdge(Baux, Bin, AuxCostDec); 8107cc2493dSspupyrev } 8117cc2493dSspupyrev } 8127cc2493dSspupyrev 8137cc2493dSspupyrev // Creating edges for every jump 8147cc2493dSspupyrev for (auto &Jump : Func.Jumps) { 8157cc2493dSspupyrev uint64_t Src = Jump.Source; 8167cc2493dSspupyrev uint64_t Dst = Jump.Target; 8177cc2493dSspupyrev if (Src != Dst) { 8187cc2493dSspupyrev uint64_t SrcOut = 3 * Src + 1; 8197cc2493dSspupyrev uint64_t DstIn = 3 * Dst; 8207cc2493dSspupyrev uint64_t Cost = Jump.IsUnlikely ? MinCostMaxFlow::AuxCostUnlikely : 0; 8217cc2493dSspupyrev Network.addEdge(SrcOut, DstIn, Cost); 8227cc2493dSspupyrev } 8237cc2493dSspupyrev } 8247cc2493dSspupyrev 8257cc2493dSspupyrev // Make sure we have a valid flow circulation 8267cc2493dSspupyrev Network.addEdge(T, S, 0); 8277cc2493dSspupyrev } 8287cc2493dSspupyrev 8297cc2493dSspupyrev /// Extract resulting block and edge counts from the flow network. 8307cc2493dSspupyrev void extractWeights(MinCostMaxFlow &Network, FlowFunction &Func) { 8317cc2493dSspupyrev uint64_t NumBlocks = Func.Blocks.size(); 8327cc2493dSspupyrev 8337cc2493dSspupyrev // Extract resulting block counts 8347cc2493dSspupyrev for (uint64_t Src = 0; Src < NumBlocks; Src++) { 8357cc2493dSspupyrev auto &Block = Func.Blocks[Src]; 8367cc2493dSspupyrev uint64_t SrcOut = 3 * Src + 1; 8377cc2493dSspupyrev int64_t Flow = 0; 8387cc2493dSspupyrev for (auto &Adj : Network.getFlow(SrcOut)) { 8397cc2493dSspupyrev uint64_t DstIn = Adj.first; 8407cc2493dSspupyrev int64_t DstFlow = Adj.second; 8417cc2493dSspupyrev bool IsAuxNode = (DstIn < 3 * NumBlocks && DstIn % 3 == 2); 8427cc2493dSspupyrev if (!IsAuxNode || Block.HasSelfEdge) { 8437cc2493dSspupyrev Flow += DstFlow; 8447cc2493dSspupyrev } 8457cc2493dSspupyrev } 8467cc2493dSspupyrev Block.Flow = Flow; 8477cc2493dSspupyrev assert(Flow >= 0 && "negative block flow"); 8487cc2493dSspupyrev } 8497cc2493dSspupyrev 8507cc2493dSspupyrev // Extract resulting jump counts 8517cc2493dSspupyrev for (auto &Jump : Func.Jumps) { 8527cc2493dSspupyrev uint64_t Src = Jump.Source; 8537cc2493dSspupyrev uint64_t Dst = Jump.Target; 8547cc2493dSspupyrev int64_t Flow = 0; 8557cc2493dSspupyrev if (Src != Dst) { 8567cc2493dSspupyrev uint64_t SrcOut = 3 * Src + 1; 8577cc2493dSspupyrev uint64_t DstIn = 3 * Dst; 8587cc2493dSspupyrev Flow = Network.getFlow(SrcOut, DstIn); 8597cc2493dSspupyrev } else { 8607cc2493dSspupyrev uint64_t SrcOut = 3 * Src + 1; 8617cc2493dSspupyrev uint64_t SrcAux = 3 * Src + 2; 8627cc2493dSspupyrev int64_t AuxFlow = Network.getFlow(SrcOut, SrcAux); 8637cc2493dSspupyrev if (AuxFlow > 0) 8647cc2493dSspupyrev Flow = AuxFlow; 8657cc2493dSspupyrev } 8667cc2493dSspupyrev Jump.Flow = Flow; 8677cc2493dSspupyrev assert(Flow >= 0 && "negative jump flow"); 8687cc2493dSspupyrev } 8697cc2493dSspupyrev } 8707cc2493dSspupyrev 8717cc2493dSspupyrev #ifndef NDEBUG 8727cc2493dSspupyrev /// Verify that the computed flow values satisfy flow conservation rules 8737cc2493dSspupyrev void verifyWeights(const FlowFunction &Func) { 8747cc2493dSspupyrev const uint64_t NumBlocks = Func.Blocks.size(); 8757cc2493dSspupyrev auto InFlow = std::vector<uint64_t>(NumBlocks, 0); 8767cc2493dSspupyrev auto OutFlow = std::vector<uint64_t>(NumBlocks, 0); 8777cc2493dSspupyrev for (auto &Jump : Func.Jumps) { 8787cc2493dSspupyrev InFlow[Jump.Target] += Jump.Flow; 8797cc2493dSspupyrev OutFlow[Jump.Source] += Jump.Flow; 8807cc2493dSspupyrev } 8817cc2493dSspupyrev 8827cc2493dSspupyrev uint64_t TotalInFlow = 0; 8837cc2493dSspupyrev uint64_t TotalOutFlow = 0; 8847cc2493dSspupyrev for (uint64_t I = 0; I < NumBlocks; I++) { 8857cc2493dSspupyrev auto &Block = Func.Blocks[I]; 8867cc2493dSspupyrev if (Block.isEntry()) { 8877cc2493dSspupyrev TotalInFlow += Block.Flow; 8887cc2493dSspupyrev assert(Block.Flow == OutFlow[I] && "incorrectly computed control flow"); 8897cc2493dSspupyrev } else if (Block.isExit()) { 8907cc2493dSspupyrev TotalOutFlow += Block.Flow; 8917cc2493dSspupyrev assert(Block.Flow == InFlow[I] && "incorrectly computed control flow"); 8927cc2493dSspupyrev } else { 8937cc2493dSspupyrev assert(Block.Flow == OutFlow[I] && "incorrectly computed control flow"); 8947cc2493dSspupyrev assert(Block.Flow == InFlow[I] && "incorrectly computed control flow"); 8957cc2493dSspupyrev } 8967cc2493dSspupyrev } 8977cc2493dSspupyrev assert(TotalInFlow == TotalOutFlow && "incorrectly computed control flow"); 89898dd2f9eSspupyrev 89998dd2f9eSspupyrev // Verify that there are no isolated flow components 90098dd2f9eSspupyrev // One could modify FlowFunction to hold edges indexed by the sources, which 90198dd2f9eSspupyrev // will avoid a creation of the object 90298dd2f9eSspupyrev auto PositiveFlowEdges = std::vector<std::vector<uint64_t>>(NumBlocks); 90398dd2f9eSspupyrev for (auto &Jump : Func.Jumps) { 90498dd2f9eSspupyrev if (Jump.Flow > 0) { 90598dd2f9eSspupyrev PositiveFlowEdges[Jump.Source].push_back(Jump.Target); 90698dd2f9eSspupyrev } 90798dd2f9eSspupyrev } 90898dd2f9eSspupyrev 90993a2c291Sspupyrev // Run BFS from the source along edges with positive flow 91098dd2f9eSspupyrev std::queue<uint64_t> Queue; 9115f4ae564SJan Svoboda auto Visited = BitVector(NumBlocks, false); 91298dd2f9eSspupyrev Queue.push(Func.Entry); 91398dd2f9eSspupyrev Visited[Func.Entry] = true; 91498dd2f9eSspupyrev while (!Queue.empty()) { 91598dd2f9eSspupyrev uint64_t Src = Queue.front(); 91698dd2f9eSspupyrev Queue.pop(); 91798dd2f9eSspupyrev for (uint64_t Dst : PositiveFlowEdges[Src]) { 91898dd2f9eSspupyrev if (!Visited[Dst]) { 91998dd2f9eSspupyrev Queue.push(Dst); 92098dd2f9eSspupyrev Visited[Dst] = true; 92198dd2f9eSspupyrev } 92298dd2f9eSspupyrev } 92398dd2f9eSspupyrev } 92498dd2f9eSspupyrev 92598dd2f9eSspupyrev // Verify that every block that has a positive flow is reached from the source 92698dd2f9eSspupyrev // along edges with a positive flow 92798dd2f9eSspupyrev for (uint64_t I = 0; I < NumBlocks; I++) { 92898dd2f9eSspupyrev auto &Block = Func.Blocks[I]; 92998dd2f9eSspupyrev assert((Visited[I] || Block.Flow == 0) && "an isolated flow component"); 93098dd2f9eSspupyrev } 9317cc2493dSspupyrev } 9327cc2493dSspupyrev #endif 9337cc2493dSspupyrev 9347cc2493dSspupyrev } // end of anonymous namespace 9357cc2493dSspupyrev 9367cc2493dSspupyrev /// Apply the profile inference algorithm for a given flow function 9377cc2493dSspupyrev void llvm::applyFlowInference(FlowFunction &Func) { 9387cc2493dSspupyrev // Create and apply an inference network model 9397cc2493dSspupyrev auto InferenceNetwork = MinCostMaxFlow(); 9407cc2493dSspupyrev initializeNetwork(InferenceNetwork, Func); 9417cc2493dSspupyrev InferenceNetwork.run(); 9427cc2493dSspupyrev 9437cc2493dSspupyrev // Extract flow values for every block and every edge 9447cc2493dSspupyrev extractWeights(InferenceNetwork, Func); 9457cc2493dSspupyrev 94698dd2f9eSspupyrev // Post-processing adjustments to the flow 94798dd2f9eSspupyrev auto Adjuster = FlowAdjuster(Func); 94898dd2f9eSspupyrev Adjuster.run(); 94998dd2f9eSspupyrev 9507cc2493dSspupyrev #ifndef NDEBUG 9517cc2493dSspupyrev // Verify the result 9527cc2493dSspupyrev verifyWeights(Func); 9537cc2493dSspupyrev #endif 9547cc2493dSspupyrev } 955