Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions llvm/include/llvm/Analysis/StaticDataProfileInfo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#ifndef LLVM_ANALYSIS_STATICDATAPROFILEINFO_H
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is reasonable to put this definition into ProfileSummaryInfo.h, as it is about summary/aggregation of const profiles.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, I don't feel strong about having new classes in ProfileSummaryInfo.h or in new files. Actually I once thought about the former and hesitated between the two.

I ended up with the latter mainly because PSI is included in multiple cpp files, and the new classes don't need to be included as much.

As the static data partitioning work is under development and symbolized data access profiles may need helper functions and libraries, I think we can keep it this way and do refactors later where they fit. What do you think about this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good.

#define LLVM_ANALYSIS_STATICDATAPROFILEINFO_H

#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/IR/Constant.h"
#include "llvm/Pass.h"

namespace llvm {

/// A class that holds the constants that represent static data and their
/// profile information and provides methods to operate on them.
class StaticDataProfileInfo {
public:
/// Accummulate the profile count of a constant that will be lowered to static
/// data sections.
DenseMap<const Constant *, uint64_t> ConstantProfileCounts;

/// Keeps track of the constants that are seen at least once without profile
/// counts.
DenseSet<const Constant *> ConstantWithoutCounts;

public:
StaticDataProfileInfo() = default;

/// If \p Count is not nullopt, add it to the profile count of the constant \p
/// C in a saturating way, and clamp the count to \p getInstrMaxCountValue if
/// the result exceeds it. Otherwise, mark the constant as having no profile
/// count.
void addConstantProfileCount(const Constant *C,
std::optional<uint64_t> Count);

/// If \p C has a count, return it. Otherwise, return std::nullopt.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When it returns nullopt, it does not mean the constant has unknown profile count -- it can just mean the profile count has not been set/aggregate for the constant. When the analysis phase is done, returning nullopt seems the same has 'hasUnknownCount()' below. Perhaps clarify in the comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get the point that the difference between hasUnknownCount and getConstantProfileCount are subtle. I thought about this more.

Basically, StaticDataAnnotator uses both PSI and this class to decide the section prefix of a constant, so an alternative is (for this class) to provide getConstantSectionPrefix directly. The updated change implements the alternative option, and makes getConstantProfileCount to a private class method.

std::optional<uint64_t> getConstantProfileCount(const Constant *C) const;

/// Return true if the constant \p C is seen at least once without profiles.
bool hasUnknownCount(const Constant *C) const {
return ConstantWithoutCounts.count(C);
}
};

/// This wraps the StaticDataProfileInfo object as an immutable pass, for a
/// backend pass to operate on.
class StaticDataProfileInfoWrapperPass : public ImmutablePass {
public:
static char ID;
StaticDataProfileInfoWrapperPass();
bool doInitialization(Module &M) override;
bool doFinalization(Module &M) override;

StaticDataProfileInfo &getStaticDataProfileInfo() { return *Info; }
const StaticDataProfileInfo &getStaticDataProfileInfo() const {
return *Info;
}

/// This pass provides StaticDataProfileInfo for reads/writes but does not
/// modify \p M or other analysis. All analysis are preserved.
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesAll();
}

private:
std::unique_ptr<StaticDataProfileInfo> Info;
};

} // namespace llvm

#endif // LLVM_ANALYSIS_STATICDATAPROFILEINFO_H
9 changes: 7 additions & 2 deletions llvm/include/llvm/CodeGen/Passes.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,15 @@ namespace llvm {
/// using profile information.
MachineFunctionPass *createMachineFunctionSplitterPass();

/// createStaticDataSplitterPass - This pass partitions a static data section
/// into a hot and cold section using profile information.
/// createStaticDataSplitterPass - This is a machine-function pass that
/// categorizes static data hotness using profile information.
MachineFunctionPass *createStaticDataSplitterPass();

/// createStaticDataAnnotatorPASS - This is a module pass that reads from
/// StaticDataProfileInfoWrapperPass and annotates the section prefix of
/// global variables.
ModulePass *createStaticDataAnnotatorPass();

/// MachineFunctionPrinter pass - This pass prints out the machine function to
/// the given stream as a debugging tool.
MachineFunctionPass *
Expand Down
2 changes: 2 additions & 0 deletions llvm/include/llvm/InitializePasses.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ void initializeMachineLoopInfoWrapperPassPass(PassRegistry &);
void initializeMachineModuleInfoWrapperPassPass(PassRegistry &);
void initializeMachineOptimizationRemarkEmitterPassPass(PassRegistry &);
void initializeMachineOutlinerPass(PassRegistry &);
void initializeStaticDataProfileInfoWrapperPassPass(PassRegistry &);
void initializeStaticDataAnnotatorPass(PassRegistry &);
void initializeMachinePipelinerPass(PassRegistry &);
void initializeMachinePostDominatorTreeWrapperPassPass(PassRegistry &);
void initializeMachineRegionInfoPassPass(PassRegistry &);
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/Passes/MachinePassRegistry.def
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ MACHINE_FUNCTION_PASS_WITH_PARAMS(
#define DUMMY_MACHINE_MODULE_PASS(NAME, PASS_NAME)
#endif
DUMMY_MACHINE_MODULE_PASS("machine-outliner", MachineOutlinerPass)
DUMMY_MACHINE_MODULE_PASS("static-data-annotator", StaticDataAnnotator)
DUMMY_MACHINE_MODULE_PASS("pseudo-probe-inserter", PseudoProbeInserterPass)
DUMMY_MACHINE_MODULE_PASS("mir-debugify", DebugifyMachineModule)
DUMMY_MACHINE_MODULE_PASS("mir-check-debugify", CheckDebugMachineModulePass)
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Analysis/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ add_llvm_component_library(LLVMAnalysis
ScalarEvolutionAliasAnalysis.cpp
ScalarEvolutionDivision.cpp
ScalarEvolutionNormalization.cpp
StaticDataProfileInfo.cpp
StackLifetime.cpp
StackSafetyAnalysis.cpp
StructuralHash.cpp
Expand Down
50 changes: 50 additions & 0 deletions llvm/lib/Analysis/StaticDataProfileInfo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include "llvm/Analysis/StaticDataProfileInfo.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/InitializePasses.h"
#include "llvm/ProfileData/InstrProf.h"
#include <sys/types.h>

using namespace llvm;
void StaticDataProfileInfo::addConstantProfileCount(
const Constant *C, std::optional<uint64_t> Count) {
if (!Count) {
ConstantWithoutCounts.insert(C);
return;
}
uint64_t &OriginalCount = ConstantProfileCounts[C];
OriginalCount += llvm::SaturatingAdd(*Count, OriginalCount);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should += be just =?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the catch! done.

// Clamp the count to getInstrMaxCountValue. InstrFDO reserves a few
// large values for special use.
if (OriginalCount > getInstrMaxCountValue())
OriginalCount = getInstrMaxCountValue();
}

std::optional<uint64_t>
StaticDataProfileInfo::getConstantProfileCount(const Constant *C) const {
auto I = ConstantProfileCounts.find(C);
if (I == ConstantProfileCounts.end())
return std::nullopt;
return I->second;
}

bool StaticDataProfileInfoWrapperPass::doInitialization(Module &M) {
Info.reset(new StaticDataProfileInfo());
return false;
}

bool StaticDataProfileInfoWrapperPass::doFinalization(Module &M) {
Info.reset();
return false;
}

INITIALIZE_PASS(StaticDataProfileInfoWrapperPass, "static-data-profile-info",
"Static Data Profile Info", false, true)

StaticDataProfileInfoWrapperPass::StaticDataProfileInfoWrapperPass()
: ImmutablePass(ID) {
initializeStaticDataProfileInfoWrapperPassPass(
*PassRegistry::getPassRegistry());
}

char StaticDataProfileInfoWrapperPass::ID = 0;
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ add_llvm_component_library(LLVMCodeGen
StackProtector.cpp
StackSlotColoring.cpp
StaticDataSplitter.cpp
StaticDataAnnotator.cpp
SwiftErrorValueTracking.cpp
SwitchLoweringUtils.cpp
TailDuplication.cpp
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/CodeGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ void llvm::initializeCodeGen(PassRegistry &Registry) {
initializeStackProtectorPass(Registry);
initializeStackSlotColoringLegacyPass(Registry);
initializeStaticDataSplitterPass(Registry);
initializeStaticDataAnnotatorPass(Registry);
initializeStripDebugMachineModulePass(Registry);
initializeTailDuplicateLegacyPass(Registry);
initializeTargetPassConfigPass(Registry);
Expand Down
119 changes: 119 additions & 0 deletions llvm/lib/CodeGen/StaticDataAnnotator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
//===- StaticDataAnnotator - Annotate static data's section prefix --------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// To reason about module-wide data hotness in a module granularity, this file
// implements a module pass StaticDataAnnotator to work coordinately with the
// StaticDataSplitter pass.
//
// The StaticDataSplitter pass is a machine function pass. It analyzes data
// hotness based on code and adds counters in the StaticDataProfileInfo.
// The StaticDataAnnotator pass is a module pass. It iterates global variables
// in the module, looks up counters from StaticDataProfileInfo and sets the
// section prefix based on profiles.
//
// The three-pass structure is implemented for practical reasons, to work around
// the limitation that a module pass based on legacy pass manager cannot make
// use of MachineBlockFrequencyInfo analysis. In the future, we can consider
// porting the StaticDataSplitter pass to a module-pass using the new pass
// manager framework. That way, analysis are lazily computed as opposed to
// eagerly scheduled, and a module pass can use MachineBlockFrequencyInfo.
//===----------------------------------------------------------------------===//

#include "llvm/Analysis/ProfileSummaryInfo.h"
#include "llvm/Analysis/StaticDataProfileInfo.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/IR/Analysis.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"

#define DEBUG_TYPE "static-data-annotator"

using namespace llvm;

class StaticDataAnnotator : public ModulePass {
public:
static char ID;

StaticDataProfileInfo *SDPI = nullptr;
const ProfileSummaryInfo *PSI = nullptr;

StaticDataAnnotator() : ModulePass(ID) {
initializeStaticDataAnnotatorPass(*PassRegistry::getPassRegistry());
}

void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequired<StaticDataProfileInfoWrapperPass>();
AU.addRequired<ProfileSummaryInfoWrapperPass>();
AU.setPreservesAll();
ModulePass::getAnalysisUsage(AU);
}

StringRef getPassName() const override { return "Static Data Annotator"; }

bool runOnModule(Module &M) override;
};

// Returns true if the global variable already has a section prefix that is the
// same as `Prefix`.
static bool alreadyHasSectionPrefix(const GlobalVariable &GV,
StringRef Prefix) {
std::optional<StringRef> SectionPrefix = GV.getSectionPrefix();
return SectionPrefix && (*SectionPrefix == Prefix);
}

bool StaticDataAnnotator::runOnModule(Module &M) {
SDPI = &getAnalysis<StaticDataProfileInfoWrapperPass>()
.getStaticDataProfileInfo();
PSI = &getAnalysis<ProfileSummaryInfoWrapperPass>().getPSI();

if (!PSI->hasProfileSummary())
return false;

bool Changed = false;
for (auto &GV : M.globals()) {
if (GV.isDeclarationForLinker())
continue;

// Skip global variables without profile counts. The module may not be
// profiled or instrumented.
auto Count = SDPI->getConstantProfileCount(&GV);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this point, will hasUnknownCount GVs return nullopt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the original code, the semantic of getConstantProfileCount and hasUnknownCount are subtle on the different combinations (i.e., whether a var is used in hot/cold/lukewarm/unprofiled functions, etc)

The updated code will query a section prefix from StaticDataProfileInfo directly there. PTAL.

if (!Count)
continue;

if (PSI->isHotCount(*Count) && !alreadyHasSectionPrefix(GV, "hot")) {
// The variable counter is hot, set 'hot' section prefix if the section
// prefix isn't hot already.
GV.setSectionPrefix("hot");
Changed = true;
} else if (PSI->isColdCount(*Count) && !SDPI->hasUnknownCount(&GV) &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the hasUnknownCount check needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!alreadyHasSectionPrefix(GV, "unlikely")) {
// The variable counter is cold, set 'unlikely' section prefix when
// 1) the section prefix isn't unlikely already, and
// 2) the variable is not seen without profile counts. The reason is that
// a variable without profile counts doesn't have all its uses profiled,
// for example when a function is not instrumented, or not sampled (new
// code paths).
GV.setSectionPrefix("unlikely");
Changed = true;
}
}

return Changed;
}

char StaticDataAnnotator::ID = 0;

INITIALIZE_PASS(StaticDataAnnotator, DEBUG_TYPE, "Static Data Annotator", false,
false)

ModulePass *llvm::createStaticDataAnnotatorPass() {
return new StaticDataAnnotator();
}
Loading