blob: 8a3f16a4f13cb8df7b642da2ab59fa14e32f6af0 [file] [log] [blame]
//===-- CSPreInliner.h - Profile guided preinliner ---------------- 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
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_LLVM_PROFGEN_PGOINLINEADVISOR_H
#define LLVM_TOOLS_LLVM_PROFGEN_PGOINLINEADVISOR_H
#include "ProfiledBinary.h"
#include "llvm/ADT/PriorityQueue.h"
#include "llvm/ProfileData/ProfileCommon.h"
#include "llvm/ProfileData/SampleProf.h"
#include "llvm/Transforms/IPO/ProfiledCallGraph.h"
#include "llvm/Transforms/IPO/SampleContextTracker.h"
using namespace llvm;
using namespace sampleprof;
namespace llvm {
namespace sampleprof {
// Inline candidate seen from profile
struct ProfiledInlineCandidate {
ProfiledInlineCandidate(const FunctionSamples *Samples, uint64_t Count,
uint32_t Size)
: CalleeSamples(Samples), CallsiteCount(Count), SizeCost(Size) {}
// Context-sensitive function profile for inline candidate
const FunctionSamples *CalleeSamples;
// Call site count for an inline candidate
// TODO: make sure entry count for context profile and call site
// target count for corresponding call are consistent.
uint64_t CallsiteCount;
// Size proxy for function under particular call context.
uint64_t SizeCost;
};
// Inline candidate comparer using call site weight
struct ProfiledCandidateComparer {
bool operator()(const ProfiledInlineCandidate &LHS,
const ProfiledInlineCandidate &RHS) {
// Always prioritize inlining zero-sized functions as they do not affect the
// size budget. This could happen when all of the callee's code is gone and
// only pseudo probes are left.
if ((LHS.SizeCost == 0 || RHS.SizeCost == 0) &&
(LHS.SizeCost != RHS.SizeCost))
return RHS.SizeCost == 0;
if (LHS.CallsiteCount != RHS.CallsiteCount)
return LHS.CallsiteCount < RHS.CallsiteCount;
if (LHS.SizeCost != RHS.SizeCost)
return LHS.SizeCost > RHS.SizeCost;
// Tie breaker using GUID so we have stable/deterministic inlining order
assert(LHS.CalleeSamples && RHS.CalleeSamples &&
"Expect non-null FunctionSamples");
return LHS.CalleeSamples->getGUID() < RHS.CalleeSamples->getGUID();
}
};
using ProfiledCandidateQueue =
PriorityQueue<ProfiledInlineCandidate, std::vector<ProfiledInlineCandidate>,
ProfiledCandidateComparer>;
// Pre-compilation inliner based on context-sensitive profile.
// The PreInliner estimates inline decision using hotness from profile
// and cost estimation from machine code size. It helps merges context
// profile globally and achieves better post-inine profile quality, which
// otherwise won't be possible for ThinLTO. It also reduce context profile
// size by only keep context that is estimated to be inlined.
class CSPreInliner {
public:
CSPreInliner(SampleContextTracker &Tracker, ProfiledBinary &Binary,
ProfileSummary *Summary);
void run();
private:
bool getInlineCandidates(ProfiledCandidateQueue &CQueue,
const FunctionSamples *FCallerContextSamples);
std::vector<FunctionId> buildTopDownOrder();
void processFunction(FunctionId Name);
bool shouldInline(ProfiledInlineCandidate &Candidate);
uint32_t getFuncSize(const ContextTrieNode *ContextNode);
bool UseContextCost;
SampleContextTracker &ContextTracker;
ProfiledBinary &Binary;
ProfileSummary *Summary;
};
} // end namespace sampleprof
} // end namespace llvm
#endif