Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
26 changes: 18 additions & 8 deletions llvm/include/llvm/Analysis/MemoryProfileInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,17 @@ AllocationType getAllocType(uint64_t TotalLifetimeAccessDensity,
/// the resulting metadata node.
MDNode *buildCallstackMetadata(ArrayRef<uint64_t> CallStack, LLVMContext &Ctx);

/// Build metadata from the provided list of full stack id and profiled size, to
/// use when reporting of hinted sizes is enabled.
MDNode *buildContextSizeMetadata(ArrayRef<ContextTotalSize> ContextSizeInfo,
LLVMContext &Ctx);

/// Returns the stack node from an MIB metadata node.
MDNode *getMIBStackNode(const MDNode *MIB);

/// Returns the allocation type from an MIB metadata node.
AllocationType getMIBAllocType(const MDNode *MIB);

/// Returns the total size from an MIB metadata node, or 0 if it was not
/// recorded.
uint64_t getMIBTotalSize(const MDNode *MIB);

/// Returns the string to use in attributes with the given type.
std::string getAllocTypeAttributeString(AllocationType Type);

Expand All @@ -55,11 +56,15 @@ class CallStackTrie {
// Allocation types for call context sharing the context prefix at this
// node.
uint8_t AllocTypes;
uint64_t TotalSize;
// If the user has requested reporting of hinted sizes, keep track of the
// associated full stack id and profiled sizes. Can have more than one
// after trimming (e.g. when building from metadata). This is only placed on
// the last (root-most) trie node for each allocation context.
std::vector<ContextTotalSize> ContextSizeInfo;
// Map of caller stack id to the corresponding child Trie node.
std::map<uint64_t, CallStackTrieNode *> Callers;
CallStackTrieNode(AllocationType Type, uint64_t TotalSize)
: AllocTypes(static_cast<uint8_t>(Type)), TotalSize(TotalSize) {}
CallStackTrieNode(AllocationType Type)
: AllocTypes(static_cast<uint8_t>(Type)) {}
};

// The node for the allocation at the root.
Expand All @@ -75,6 +80,11 @@ class CallStackTrie {
delete Node;
}

// Recursively build up a complete list of context size information from the
// trie nodes reached form the given Node, for hint size reporting.
void collectContextSizeInfo(CallStackTrieNode *Node,
std::vector<ContextTotalSize> &ContextSizeInfo);

// Recursive helper to trim contexts and create metadata nodes.
bool buildMIBNodes(CallStackTrieNode *Node, LLVMContext &Ctx,
std::vector<uint64_t> &MIBCallStack,
Expand All @@ -93,7 +103,7 @@ class CallStackTrie {
/// allocation call down to the bottom of the call stack (i.e. callee to
/// caller order).
void addCallStack(AllocationType AllocType, ArrayRef<uint64_t> StackIds,
uint64_t TotalSize = 0);
std::vector<ContextTotalSize> ContextSizeInfo = {});

/// Add the call stack context along with its allocation type from the MIB
/// metadata to the Trie.
Expand Down
15 changes: 13 additions & 2 deletions llvm/include/llvm/Bitcode/LLVMBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ enum GlobalValueSummarySymtabCodes {
FS_PERMODULE_CALLSITE_INFO = 26,
// Summary of per-module allocation memprof metadata.
// [nummib, nummib x (alloc type, numstackids, numstackids x stackidindex),
// [nummib x total size]?]
// [nummib x (numcontext x total size)]?]
FS_PERMODULE_ALLOC_INFO = 27,
// Summary of combined index memprof callsite metadata.
// [valueid, numstackindices, numver,
Expand All @@ -317,9 +317,20 @@ enum GlobalValueSummarySymtabCodes {
// Summary of combined index allocation memprof metadata.
// [nummib, numver,
// nummib x (alloc type, numstackids, numstackids x stackidindex),
// numver x version, [nummib x total size]?]
// numver x version]
FS_COMBINED_ALLOC_INFO = 29,
// List of all stack ids referenced by index in the callsite and alloc infos.
// [n x stack id]
FS_STACK_IDS = 30,
// List of all full stack id pairs corresponding to the total sizes recorded
// at the end of the alloc info when reporting of hinted bytes is enabled.
// We use a fixed-width array, which is more efficient as these ids typically
// are close to 64 bits in size. The max fixed width value supported is 32
// bits so each 64-bit context id hash is recorded as a pair (upper 32 bits
// first). This record must immediately precede the associated alloc info, and
// the entries must be in the exact same order as the corresponding sizes.
// [nummib x (numcontext x full stack id)]
FS_ALLOC_CONTEXT_IDS = 31,
};

enum MetadataCodes {
Expand Down
38 changes: 27 additions & 11 deletions llvm/include/llvm/IR/ModuleSummaryIndex.h
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,14 @@ template <> struct DenseMapInfo<ValueInfo> {
static unsigned getHashValue(ValueInfo I) { return (uintptr_t)I.getRef(); }
};

// For optional hinted size reporting, holds a pair of the full stack id
// (pre-trimming, from the full context in the profile), and the associated
// total profiled size.
struct ContextTotalSize {
uint64_t FullStackId;
uint64_t TotalSize;
};

/// Summary of memprof callsite metadata.
struct CallsiteInfo {
// Actual callee function.
Expand Down Expand Up @@ -408,9 +416,13 @@ struct AllocInfo {
// Vector of MIBs in this memprof metadata.
std::vector<MIBInfo> MIBs;

// If requested, keep track of total profiled sizes for each MIB. This will be
// a vector of the same length and order as the MIBs vector, if non-empty.
std::vector<uint64_t> TotalSizes;
// If requested, keep track of full stack contexts and total profiled sizes
// for each MIB. This will be a vector of the same length and order as the
// MIBs vector, if non-empty. Note that each MIB in the summary can have
// multiple of these as we trim the contexts when possible during matching.
// For hinted size reporting we, however, want the original pre-trimmed full
// stack context id for better correlation with the profile.
std::vector<std::vector<ContextTotalSize>> ContextSizeInfos;

AllocInfo(std::vector<MIBInfo> MIBs) : MIBs(std::move(MIBs)) {
Versions.push_back(0);
Expand All @@ -432,14 +444,18 @@ inline raw_ostream &operator<<(raw_ostream &OS, const AllocInfo &AE) {
for (auto &M : AE.MIBs) {
OS << "\t\t" << M << "\n";
}
if (!AE.TotalSizes.empty()) {
OS << " TotalSizes per MIB:\n\t\t";
First = true;
for (uint64_t TS : AE.TotalSizes) {
if (!First)
OS << ", ";
First = false;
OS << TS << "\n";
if (!AE.ContextSizeInfos.empty()) {
OS << "\tContextSizeInfo per MIB:\n";
for (auto Infos : AE.ContextSizeInfos) {
OS << "\t\t";
bool FirstInfo = true;
for (auto [FullStackId, TotalSize] : Infos) {
if (!FirstInfo)
OS << ", ";
FirstInfo = false;
OS << "{ " << FullStackId << ", " << TotalSize << " }";
}
OS << "\n";
}
}
return OS;
Expand Down
87 changes: 61 additions & 26 deletions llvm/lib/Analysis/MemoryProfileInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,6 @@ AllocationType llvm::memprof::getMIBAllocType(const MDNode *MIB) {
return AllocationType::NotCold;
}

uint64_t llvm::memprof::getMIBTotalSize(const MDNode *MIB) {
if (MIB->getNumOperands() < 3)
return 0;
return mdconst::dyn_extract<ConstantInt>(MIB->getOperand(2))->getZExtValue();
}

std::string llvm::memprof::getAllocTypeAttributeString(AllocationType Type) {
switch (Type) {
case AllocationType::NotCold:
Expand Down Expand Up @@ -135,22 +129,21 @@ bool llvm::memprof::hasSingleAllocType(uint8_t AllocTypes) {
return NumAllocTypes == 1;
}

void CallStackTrie::addCallStack(AllocationType AllocType,
ArrayRef<uint64_t> StackIds,
uint64_t TotalSize) {
void CallStackTrie::addCallStack(
AllocationType AllocType, ArrayRef<uint64_t> StackIds,
std::vector<ContextTotalSize> ContextSizeInfo) {
bool First = true;
CallStackTrieNode *Curr = nullptr;
for (auto StackId : StackIds) {
// If this is the first stack frame, add or update alloc node.
// If this is the first stack frame, add or update alloc node.
if (First) {
First = false;
if (Alloc) {
assert(AllocStackId == StackId);
Alloc->AllocTypes |= static_cast<uint8_t>(AllocType);
Alloc->TotalSize += TotalSize;
} else {
AllocStackId = StackId;
Alloc = new CallStackTrieNode(AllocType, TotalSize);
Alloc = new CallStackTrieNode(AllocType);
}
Curr = Alloc;
continue;
Expand All @@ -160,15 +153,18 @@ void CallStackTrie::addCallStack(AllocationType AllocType,
if (Next != Curr->Callers.end()) {
Curr = Next->second;
Curr->AllocTypes |= static_cast<uint8_t>(AllocType);
Curr->TotalSize += TotalSize;
continue;
}
// Otherwise add a new caller node.
auto *New = new CallStackTrieNode(AllocType, TotalSize);
auto *New = new CallStackTrieNode(AllocType);
Curr->Callers[StackId] = New;
Curr = New;
}
assert(Curr);
Curr->ContextSizeInfo.insert(Curr->ContextSizeInfo.end(),
ContextSizeInfo.begin(), ContextSizeInfo.end());
std::vector<ContextTotalSize> AllContextSizeInfo;
collectContextSizeInfo(Curr, AllContextSizeInfo);
}

void CallStackTrie::addCallStack(MDNode *MIB) {
Expand All @@ -181,21 +177,52 @@ void CallStackTrie::addCallStack(MDNode *MIB) {
assert(StackId);
CallStack.push_back(StackId->getZExtValue());
}
addCallStack(getMIBAllocType(MIB), CallStack, getMIBTotalSize(MIB));
std::vector<ContextTotalSize> ContextSizeInfo;
// Collect the context size information if it exists.
if (MIB->getNumOperands() > 2) {
for (unsigned I = 2; I < MIB->getNumOperands(); I++) {
MDNode *ContextSizePair = dyn_cast<MDNode>(MIB->getOperand(I));
assert(ContextSizePair->getNumOperands() == 2);
uint64_t FullStackId =
mdconst::dyn_extract<ConstantInt>(ContextSizePair->getOperand(0))
->getZExtValue();
uint64_t TotalSize =
mdconst::dyn_extract<ConstantInt>(ContextSizePair->getOperand(1))
->getZExtValue();
ContextSizeInfo.push_back({FullStackId, TotalSize});
}
}
addCallStack(getMIBAllocType(MIB), CallStack, std::move(ContextSizeInfo));
}

static MDNode *createMIBNode(LLVMContext &Ctx, ArrayRef<uint64_t> MIBCallStack,
AllocationType AllocType, uint64_t TotalSize) {
AllocationType AllocType,
ArrayRef<ContextTotalSize> ContextSizeInfo) {
SmallVector<Metadata *> MIBPayload(
{buildCallstackMetadata(MIBCallStack, Ctx)});
MIBPayload.push_back(
MDString::get(Ctx, getAllocTypeAttributeString(AllocType)));
if (TotalSize)
MIBPayload.push_back(ValueAsMetadata::get(
ConstantInt::get(Type::getInt64Ty(Ctx), TotalSize)));
if (!ContextSizeInfo.empty()) {
for (const auto &[FullStackId, TotalSize] : ContextSizeInfo) {
auto *FullStackIdMD = ValueAsMetadata::get(
ConstantInt::get(Type::getInt64Ty(Ctx), FullStackId));
auto *TotalSizeMD = ValueAsMetadata::get(
ConstantInt::get(Type::getInt64Ty(Ctx), TotalSize));
auto *ContextSizeMD = MDNode::get(Ctx, {FullStackIdMD, TotalSizeMD});
MIBPayload.push_back(ContextSizeMD);
}
}
return MDNode::get(Ctx, MIBPayload);
}

void CallStackTrie::collectContextSizeInfo(
CallStackTrieNode *Node, std::vector<ContextTotalSize> &ContextSizeInfo) {
ContextSizeInfo.insert(ContextSizeInfo.end(), Node->ContextSizeInfo.begin(),
Node->ContextSizeInfo.end());
for (auto &Caller : Node->Callers)
collectContextSizeInfo(Caller.second, ContextSizeInfo);
}

// Recursive helper to trim contexts and create metadata nodes.
// Caller should have pushed Node's loc to MIBCallStack. Doing this in the
// caller makes it simpler to handle the many early returns in this method.
Expand All @@ -206,8 +233,10 @@ bool CallStackTrie::buildMIBNodes(CallStackTrieNode *Node, LLVMContext &Ctx,
// Trim context below the first node in a prefix with a single alloc type.
// Add an MIB record for the current call stack prefix.
if (hasSingleAllocType(Node->AllocTypes)) {
std::vector<ContextTotalSize> ContextSizeInfo;
collectContextSizeInfo(Node, ContextSizeInfo);
MIBNodes.push_back(createMIBNode(
Ctx, MIBCallStack, (AllocationType)Node->AllocTypes, Node->TotalSize));
Ctx, MIBCallStack, (AllocationType)Node->AllocTypes, ContextSizeInfo));
return true;
}

Expand Down Expand Up @@ -243,8 +272,10 @@ bool CallStackTrie::buildMIBNodes(CallStackTrieNode *Node, LLVMContext &Ctx,
// non-cold allocation type.
if (!CalleeHasAmbiguousCallerContext)
return false;
std::vector<ContextTotalSize> ContextSizeInfo;
collectContextSizeInfo(Node, ContextSizeInfo);
MIBNodes.push_back(createMIBNode(Ctx, MIBCallStack, AllocationType::NotCold,
Node->TotalSize));
ContextSizeInfo));
return true;
}

Expand All @@ -256,11 +287,15 @@ bool CallStackTrie::buildAndAttachMIBMetadata(CallBase *CI) {
if (hasSingleAllocType(Alloc->AllocTypes)) {
addAllocTypeAttribute(Ctx, CI, (AllocationType)Alloc->AllocTypes);
if (MemProfReportHintedSizes) {
assert(Alloc->TotalSize);
errs() << "Total size for allocation with location hash " << AllocStackId
<< " and single alloc type "
<< getAllocTypeAttributeString((AllocationType)Alloc->AllocTypes)
<< ": " << Alloc->TotalSize << "\n";
std::vector<ContextTotalSize> ContextSizeInfo;
collectContextSizeInfo(Alloc, ContextSizeInfo);
for (const auto &[FullStackId, TotalSize] : ContextSizeInfo) {
errs()
<< "MemProf hinting: Total size for full allocation context hash "
<< FullStackId << " and single alloc type "
<< getAllocTypeAttributeString((AllocationType)Alloc->AllocTypes)
<< ": " << TotalSize << "\n";
}
}
return false;
}
Expand Down
31 changes: 23 additions & 8 deletions llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,7 @@ static void computeFunctionSummary(
if (MemProfMD) {
std::vector<MIBInfo> MIBs;
std::vector<uint64_t> TotalSizes;
std::vector<std::vector<ContextTotalSize>> ContextSizeInfos;
for (auto &MDOp : MemProfMD->operands()) {
auto *MIBMD = cast<const MDNode>(MDOp);
MDNode *StackNode = getMIBStackNode(MIBMD);
Expand All @@ -540,18 +541,32 @@ static void computeFunctionSummary(
if (StackIdIndices.empty() || StackIdIndices.back() != StackIdIdx)
StackIdIndices.push_back(StackIdIdx);
}
// If we have context size information, collect it for inclusion in
// the summary.
assert(MIBMD->getNumOperands() > 2 || !MemProfReportHintedSizes);
if (MIBMD->getNumOperands() > 2) {
std::vector<ContextTotalSize> ContextSizes;
for (unsigned I = 2; I < MIBMD->getNumOperands(); I++) {
MDNode *ContextSizePair = dyn_cast<MDNode>(MIBMD->getOperand(I));
assert(ContextSizePair->getNumOperands() == 2);
uint64_t FullStackId = mdconst::dyn_extract<ConstantInt>(
ContextSizePair->getOperand(0))
->getZExtValue();
uint64_t TS = mdconst::dyn_extract<ConstantInt>(
ContextSizePair->getOperand(1))
->getZExtValue();
ContextSizes.push_back({FullStackId, TS});
}
ContextSizeInfos.push_back(std::move(ContextSizes));
}
MIBs.push_back(
MIBInfo(getMIBAllocType(MIBMD), std::move(StackIdIndices)));
if (MemProfReportHintedSizes) {
auto TotalSize = getMIBTotalSize(MIBMD);
assert(TotalSize);
TotalSizes.push_back(TotalSize);
}
}
Allocs.push_back(AllocInfo(std::move(MIBs)));
if (MemProfReportHintedSizes) {
assert(Allocs.back().MIBs.size() == TotalSizes.size());
Allocs.back().TotalSizes = std::move(TotalSizes);
assert(!ContextSizeInfos.empty() || !MemProfReportHintedSizes);
if (!ContextSizeInfos.empty()) {
assert(Allocs.back().MIBs.size() == ContextSizeInfos.size());
Allocs.back().ContextSizeInfos = std::move(ContextSizeInfos);
}
} else if (!InstCallsite.empty()) {
SmallVector<unsigned> StackIdIndices;
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ GetCodeName(unsigned CodeID, unsigned BlockID,
STRINGIFY_CODE(FS, COMBINED_CALLSITE_INFO)
STRINGIFY_CODE(FS, COMBINED_ALLOC_INFO)
STRINGIFY_CODE(FS, STACK_IDS)
STRINGIFY_CODE(FS, ALLOC_CONTEXT_IDS)
}
case bitc::METADATA_ATTACHMENT_ID:
switch (CodeID) {
Expand Down
Loading
Loading