//===- bolt/Passes/HFSort.h - Cluster functions by hotness ------*- 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
//
//===----------------------------------------------------------------------===//
//
// Implementation of HFSort algorithm for function ordering:
// https://research.fb.com/wp-content/uploads/2017/01/cgo2017-hfsort-final1.pdf
//
// Cluster functions by hotness.  There are four clustering algorithms:
// 1. clusterize
// 2. HFsort+
// 3. pettisAndHansen
// 4. randomClusters
//
//===----------------------------------------------------------------------===//

#ifndef BOLT_PASSES_HFSORT_H
#define BOLT_PASSES_HFSORT_H

#include "bolt/Passes/CallGraph.h"

#include <string>
#include <vector>

namespace llvm {
namespace bolt {

class Cluster {
public:
  Cluster(CallGraph::NodeId Id, const CallGraph::Node &F);
  Cluster(const std::vector<CallGraph::NodeId> &Nodes, const CallGraph &Cg);

  std::string toString() const;
  double density() const { return Density; }
  uint64_t samples() const { return Samples; }
  uint32_t size() const { return Size; }
  bool frozen() const { return Frozen; }
  void freeze() { Frozen = true; }
  void merge(const Cluster &Other, const double Aw = 0);
  void merge(const Cluster &Other,
             const std::vector<CallGraph::NodeId> &Targets_);
  void clear();
  size_t numTargets() const { return Targets.size(); }
  const std::vector<CallGraph::NodeId> &targets() const { return Targets; }
  CallGraph::NodeId target(size_t N) const { return Targets[N]; }
  void reverseTargets();
  bool hasId() const { return Id != -1u; }
  void setId(uint32_t NewId) {
    assert(!hasId());
    Id = NewId;
  }
  uint32_t id() const {
    assert(hasId());
    return Id;
  }

private:
  uint32_t Id{-1u};
  std::vector<CallGraph::NodeId> Targets;
  uint64_t Samples{0};
  uint32_t Size{0};
  double Density{0.0};
  bool Frozen{false}; // not a candidate for merging
};

// Maximum size of a cluster, in bytes.
constexpr uint32_t MaxClusterSize = 1 << 20;

// Size of a huge page in bytes.
constexpr uint32_t HugePageSize = 2 << 20;

inline bool compareClustersDensity(const Cluster &C1, const Cluster &C2) {
  return C1.density() > C2.density();
}

/*
 * Cluster functions in order to minimize call distance.
 */
std::vector<Cluster> clusterize(const CallGraph &Cg);

/*
 * Optimize function placement prioritizing i-TLB and i-cache performance.
 */
std::vector<Cluster> hfsortPlus(CallGraph &Cg);

/*
 * Pettis-Hansen code layout algorithm
 * reference: K. Pettis and R. C. Hansen, "Profile Guided Code Positioning",
 * PLDI '90
 */
std::vector<Cluster> pettisAndHansen(const CallGraph &Cg);

/* Group functions into clusters randomly. */
std::vector<Cluster> randomClusters(const CallGraph &Cg);

} // end namespace bolt
} // end namespace llvm

#endif // BOLT_PASSES_HFSORT_H
