//===- Transforms/Utils/SampleProfileInference.h ----------*- C++ -*-===//
 
//
 
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 
// See https://llvm.org/LICENSE.txt for license information.
 
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
//
 
//===----------------------------------------------------------------------===//
 
//
 
/// \file
 
/// This file provides the interface for the profile inference algorithm, profi.
 
//
 
//===----------------------------------------------------------------------===//
 
 
 
#ifndef LLVM_TRANSFORMS_UTILS_SAMPLEPROFILEINFERENCE_H
 
#define LLVM_TRANSFORMS_UTILS_SAMPLEPROFILEINFERENCE_H
 
 
 
#include "llvm/ADT/DenseMap.h"
 
#include "llvm/ADT/DepthFirstIterator.h"
 
#include "llvm/ADT/SmallVector.h"
 
 
 
#include "llvm/IR/BasicBlock.h"
 
#include "llvm/IR/Instruction.h"
 
#include "llvm/IR/Instructions.h"
 
 
 
namespace llvm {
 
 
 
class Function;
 
class MachineBasicBlock;
 
class MachineFunction;
 
 
 
namespace afdo_detail {
 
 
 
template <class BlockT> struct TypeMap {};
 
template <> struct TypeMap<BasicBlock> {
 
  using BasicBlockT = BasicBlock;
 
  using FunctionT = Function;
 
};
 
template <> struct TypeMap<MachineBasicBlock> {
 
  using BasicBlockT = MachineBasicBlock;
 
  using FunctionT = MachineFunction;
 
};
 
 
 
} // end namespace afdo_detail
 
 
 
struct FlowJump;
 
 
 
/// A wrapper of a binary basic block.
 
struct FlowBlock {
 
  uint64_t Index;
 
  uint64_t Weight{0};
 
  bool HasUnknownWeight{true};
 
  bool IsUnlikely{false};
 
  uint64_t Flow{0};
 
  std::vector<FlowJump *> SuccJumps;
 
  std::vector<FlowJump *> PredJumps;
 
 
 
  /// Check if it is the entry block in the function.
 
  bool isEntry() const { return PredJumps.empty(); }
 
 
 
  /// Check if it is an exit block in the function.
 
  bool isExit() const { return SuccJumps.empty(); }
 
};
 
 
 
/// A wrapper of a jump between two basic blocks.
 
struct FlowJump {
 
  uint64_t Source;
 
  uint64_t Target;
 
  uint64_t Weight{0};
 
  bool HasUnknownWeight{true};
 
  bool IsUnlikely{false};
 
  uint64_t Flow{0};
 
};
 
 
 
/// A wrapper of binary function with basic blocks and jumps.
 
struct FlowFunction {
 
  /// Basic blocks in the function.
 
  std::vector<FlowBlock> Blocks;
 
  /// Jumps between the basic blocks.
 
  std::vector<FlowJump> Jumps;
 
  /// The index of the entry block.
 
  uint64_t Entry{0};
 
};
 
 
 
/// Various thresholds and options controlling the behavior of the profile
 
/// inference algorithm. Default values are tuned for several large-scale
 
/// applications, and can be modified via corresponding command-line flags.
 
struct ProfiParams {
 
  /// Evenly distribute flow when there are multiple equally likely options.
 
  bool EvenFlowDistribution{false};
 
 
 
  /// Evenly re-distribute flow among unknown subgraphs.
 
  bool RebalanceUnknown{false};
 
 
 
  /// Join isolated components having positive flow.
 
  bool JoinIslands{false};
 
 
 
  /// The cost of increasing a block's count by one.
 
  unsigned CostBlockInc{0};
 
 
 
  /// The cost of decreasing a block's count by one.
 
  unsigned CostBlockDec{0};
 
 
 
  /// The cost of increasing a count of zero-weight block by one.
 
  unsigned CostBlockZeroInc{0};
 
 
 
  /// The cost of increasing the entry block's count by one.
 
  unsigned CostBlockEntryInc{0};
 
 
 
  /// The cost of decreasing the entry block's count by one.
 
  unsigned CostBlockEntryDec{0};
 
 
 
  /// The cost of increasing an unknown block's count by one.
 
  unsigned CostBlockUnknownInc{0};
 
 
 
  /// The cost of increasing a jump's count by one.
 
  unsigned CostJumpInc{0};
 
 
 
  /// The cost of increasing a fall-through jump's count by one.
 
  unsigned CostJumpFTInc{0};
 
 
 
  /// The cost of decreasing a jump's count by one.
 
  unsigned CostJumpDec{0};
 
 
 
  /// The cost of decreasing a fall-through jump's count by one.
 
  unsigned CostJumpFTDec{0};
 
 
 
  /// The cost of increasing an unknown jump's count by one.
 
  unsigned CostJumpUnknownInc{0};
 
 
 
  /// The cost of increasing an unknown fall-through jump's count by one.
 
  unsigned CostJumpUnknownFTInc{0};
 
 
 
  /// The cost of taking an unlikely block/jump.
 
  const int64_t CostUnlikely = ((int64_t)1) << 30;
 
};
 
 
 
void applyFlowInference(const ProfiParams &Params, FlowFunction &Func);
 
void applyFlowInference(FlowFunction &Func);
 
 
 
/// Sample profile inference pass.
 
template <typename BT> class SampleProfileInference {
 
public:
 
  using BasicBlockT = typename afdo_detail::TypeMap<BT>::BasicBlockT;
 
  using FunctionT = typename afdo_detail::TypeMap<BT>::FunctionT;
 
  using Edge = std::pair<const BasicBlockT *, const BasicBlockT *>;
 
  using BlockWeightMap = DenseMap<const BasicBlockT *, uint64_t>;
 
  using EdgeWeightMap = DenseMap<Edge, uint64_t>;
 
  using BlockEdgeMap =
 
      DenseMap<const BasicBlockT *, SmallVector<const BasicBlockT *, 8>>;
 
 
 
  SampleProfileInference(FunctionT &F, BlockEdgeMap &Successors,
 
                         BlockWeightMap &SampleBlockWeights)
 
      : F(F), Successors(Successors), SampleBlockWeights(SampleBlockWeights) {}
 
 
 
  /// Apply the profile inference algorithm for a given function
 
  void apply(BlockWeightMap &BlockWeights, EdgeWeightMap &EdgeWeights);
 
 
 
private:
 
  /// Initialize flow function blocks, jumps and misc metadata.
 
  void initFunction(FlowFunction &Func,
 
                    const std::vector<const BasicBlockT *> &BasicBlocks,
 
                    DenseMap<const BasicBlockT *, uint64_t> &BlockIndex);
 
 
 
  /// Try to infer branch probabilities mimicking implementation of
 
  /// BranchProbabilityInfo. Unlikely taken branches are marked so that the
 
  /// inference algorithm can avoid sending flow along corresponding edges.
 
  void findUnlikelyJumps(const std::vector<const BasicBlockT *> &BasicBlocks,
 
                         BlockEdgeMap &Successors, FlowFunction &Func);
 
 
 
  /// Determine whether the block is an exit in the CFG.
 
  bool isExit(const BasicBlockT *BB);
 
 
 
  /// Function.
 
  const FunctionT &F;
 
 
 
  /// Successors for each basic block in the CFG.
 
  BlockEdgeMap &Successors;
 
 
 
  /// Map basic blocks to their sampled weights.
 
  BlockWeightMap &SampleBlockWeights;
 
};
 
 
 
template <typename BT>
 
void SampleProfileInference<BT>::apply(BlockWeightMap &BlockWeights,
 
                                       EdgeWeightMap &EdgeWeights) {
 
  // Find all forwards reachable blocks which the inference algorithm will be
 
  // applied on.
 
  df_iterator_default_set<const BasicBlockT *> Reachable;
 
  for (auto *BB : depth_first_ext(&F, Reachable))
 
    (void)BB /* Mark all reachable blocks */;
 
 
 
  // Find all backwards reachable blocks which the inference algorithm will be
 
  // applied on.
 
  df_iterator_default_set<const BasicBlockT *> InverseReachable;
 
  for (const auto &BB : F) {
 
    // An exit block is a block without any successors.
 
    if (isExit(&BB)) {
 
      for (auto *RBB : inverse_depth_first_ext(&BB, InverseReachable))
 
        (void)RBB;
 
    }
 
  }
 
 
 
  // Keep a stable order for reachable blocks
 
  DenseMap<const BasicBlockT *, uint64_t> BlockIndex;
 
  std::vector<const BasicBlockT *> BasicBlocks;
 
  BlockIndex.reserve(Reachable.size());
 
  BasicBlocks.reserve(Reachable.size());
 
  for (const auto &BB : F) {
 
    if (Reachable.count(&BB) && InverseReachable.count(&BB)) {
 
      BlockIndex[&BB] = BasicBlocks.size();
 
      BasicBlocks.push_back(&BB);
 
    }
 
  }
 
 
 
  BlockWeights.clear();
 
  EdgeWeights.clear();
 
  bool HasSamples = false;
 
  for (const auto *BB : BasicBlocks) {
 
    auto It = SampleBlockWeights.find(BB);
 
    if (It != SampleBlockWeights.end() && It->second > 0) {
 
      HasSamples = true;
 
      BlockWeights[BB] = It->second;
 
    }
 
  }
 
  // Quit early for functions with a single block or ones w/o samples
 
  if (BasicBlocks.size() <= 1 || !HasSamples) {
 
    return;
 
  }
 
 
 
  // Create necessary objects
 
  FlowFunction Func;
 
  initFunction(Func, BasicBlocks, BlockIndex);
 
 
 
  // Create and apply the inference network model.
 
  applyFlowInference(Func);
 
 
 
  // Extract the resulting weights from the control flow
 
  // All weights are increased by one to avoid propagation errors introduced by
 
  // zero weights.
 
  for (const auto *BB : BasicBlocks) {
 
    BlockWeights[BB] = Func.Blocks[BlockIndex[BB]].Flow;
 
  }
 
  for (auto &Jump : Func.Jumps) {
 
    Edge E = std::make_pair(BasicBlocks[Jump.Source], BasicBlocks[Jump.Target]);
 
    EdgeWeights[E] = Jump.Flow;
 
  }
 
 
 
#ifndef NDEBUG
 
  // Unreachable blocks and edges should not have a weight.
 
  for (auto &I : BlockWeights) {
 
    assert(Reachable.contains(I.first));
 
    assert(InverseReachable.contains(I.first));
 
  }
 
  for (auto &I : EdgeWeights) {
 
    assert(Reachable.contains(I.first.first) &&
 
           Reachable.contains(I.first.second));
 
    assert(InverseReachable.contains(I.first.first) &&
 
           InverseReachable.contains(I.first.second));
 
  }
 
#endif
 
}
 
 
 
template <typename BT>
 
void SampleProfileInference<BT>::initFunction(
 
    FlowFunction &Func, const std::vector<const BasicBlockT *> &BasicBlocks,
 
    DenseMap<const BasicBlockT *, uint64_t> &BlockIndex) {
 
  Func.Blocks.reserve(BasicBlocks.size());
 
  // Create FlowBlocks
 
  for (const auto *BB : BasicBlocks) {
 
    FlowBlock Block;
 
    if (SampleBlockWeights.find(BB) != SampleBlockWeights.end()) {
 
      Block.HasUnknownWeight = false;
 
      Block.Weight = SampleBlockWeights[BB];
 
    } else {
 
      Block.HasUnknownWeight = true;
 
      Block.Weight = 0;
 
    }
 
    Block.Index = Func.Blocks.size();
 
    Func.Blocks.push_back(Block);
 
  }
 
  // Create FlowEdges
 
  for (const auto *BB : BasicBlocks) {
 
    for (auto *Succ : Successors[BB]) {
 
      if (!BlockIndex.count(Succ))
 
        continue;
 
      FlowJump Jump;
 
      Jump.Source = BlockIndex[BB];
 
      Jump.Target = BlockIndex[Succ];
 
      Func.Jumps.push_back(Jump);
 
    }
 
  }
 
  for (auto &Jump : Func.Jumps) {
 
    uint64_t Src = Jump.Source;
 
    uint64_t Dst = Jump.Target;
 
    Func.Blocks[Src].SuccJumps.push_back(&Jump);
 
    Func.Blocks[Dst].PredJumps.push_back(&Jump);
 
  }
 
 
 
  // Try to infer probabilities of jumps based on the content of basic block
 
  findUnlikelyJumps(BasicBlocks, Successors, Func);
 
 
 
  // Find the entry block
 
  for (size_t I = 0; I < Func.Blocks.size(); I++) {
 
    if (Func.Blocks[I].isEntry()) {
 
      Func.Entry = I;
 
      break;
 
    }
 
  }
 
  assert(Func.Entry == 0 && "incorrect index of the entry block");
 
 
 
  // Pre-process data: make sure the entry weight is at least 1
 
  auto &EntryBlock = Func.Blocks[Func.Entry];
 
  if (EntryBlock.Weight == 0 && !EntryBlock.HasUnknownWeight) {
 
    EntryBlock.Weight = 1;
 
    EntryBlock.HasUnknownWeight = false;
 
  }
 
}
 
 
 
template <typename BT>
 
inline void SampleProfileInference<BT>::findUnlikelyJumps(
 
    const std::vector<const BasicBlockT *> &BasicBlocks,
 
    BlockEdgeMap &Successors, FlowFunction &Func) {}
 
 
 
template <>
 
inline void SampleProfileInference<BasicBlock>::findUnlikelyJumps(
 
    const std::vector<const BasicBlockT *> &BasicBlocks,
 
    BlockEdgeMap &Successors, FlowFunction &Func) {
 
  for (auto &Jump : Func.Jumps) {
 
    const auto *BB = BasicBlocks[Jump.Source];
 
    const auto *Succ = BasicBlocks[Jump.Target];
 
    const Instruction *TI = BB->getTerminator();
 
    // Check if a block ends with InvokeInst and mark non-taken branch unlikely.
 
    // In that case block Succ should be a landing pad
 
    if (Successors[BB].size() == 2 && Successors[BB].back() == Succ) {
 
      if (isa<InvokeInst>(TI)) {
 
        Jump.IsUnlikely = true;
 
      }
 
    }
 
    const Instruction *SuccTI = Succ->getTerminator();
 
    // Check if the target block contains UnreachableInst and mark it unlikely
 
    if (SuccTI->getNumSuccessors() == 0) {
 
      if (isa<UnreachableInst>(SuccTI)) {
 
        Jump.IsUnlikely = true;
 
      }
 
    }
 
  }
 
}
 
 
 
template <typename BT>
 
inline bool SampleProfileInference<BT>::isExit(const BasicBlockT *BB) {
 
  return BB->succ_empty();
 
}
 
 
 
template <>
 
inline bool SampleProfileInference<BasicBlock>::isExit(const BasicBlock *BB) {
 
  return succ_empty(BB);
 
}
 
 
 
} // end namespace llvm
 
#endif // LLVM_TRANSFORMS_UTILS_SAMPLEPROFILEINFERENCE_H