Skip to content

Commit c40ee0f

Browse files
Reapply "[MemProf] Add ambigous memprof attribute" (#161717) (#161918)
Reapply #157204 with fix and a new test for the issue it caused (the test change provoked the assert that was converted to an if condition). Also, make the application of this new attribute under an (on by default) flag, so that it can be more easily disabled if needed. Add test for the new flag.
1 parent 4a2f29d commit c40ee0f

File tree

6 files changed

+73
-17
lines changed

6 files changed

+73
-17
lines changed

llvm/include/llvm/Analysis/MemoryProfileInfo.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ LLVM_ABI std::string getAllocTypeAttributeString(AllocationType Type);
5959
/// True if the AllocTypes bitmask contains just a single type.
6060
LLVM_ABI bool hasSingleAllocType(uint8_t AllocTypes);
6161

62+
/// Removes any existing "ambiguous" memprof attribute. Called before we apply a
63+
/// specific allocation type such as "cold", "notcold", or "hot".
64+
LLVM_ABI void removeAnyExistingAmbiguousAttribute(CallBase *CB);
65+
66+
/// Adds an "ambiguous" memprof attribute to call with a matched allocation
67+
/// profile but that we haven't yet been able to disambiguate.
68+
LLVM_ABI void addAmbiguousAttribute(CallBase *CB);
69+
6270
/// Class to build a trie of call stack contexts for a particular profiled
6371
/// allocation call, along with their associated allocation types.
6472
/// The allocation will be at the root of the trie, which is then used to

llvm/lib/Analysis/MemoryProfileInfo.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ cl::opt<unsigned> MinPercentMaxColdSize(
5454
"memprof-min-percent-max-cold-size", cl::init(100), cl::Hidden,
5555
cl::desc("Min percent of max cold bytes for critical cold context"));
5656

57+
LLVM_ABI cl::opt<bool> MemProfUseAmbiguousAttributes(
58+
"memprof-ambiguous-attributes", cl::init(true), cl::Hidden,
59+
cl::desc("Apply ambiguous memprof attribute to ambiguous allocations"));
60+
5761
} // end namespace llvm
5862

5963
bool llvm::memprof::metadataIncludesAllContextSizeInfo() {
@@ -125,6 +129,26 @@ bool llvm::memprof::hasSingleAllocType(uint8_t AllocTypes) {
125129
return NumAllocTypes == 1;
126130
}
127131

132+
void llvm::memprof::removeAnyExistingAmbiguousAttribute(CallBase *CB) {
133+
if (!CB->hasFnAttr("memprof"))
134+
return;
135+
assert(CB->getFnAttr("memprof").getValueAsString() == "ambiguous");
136+
CB->removeFnAttr("memprof");
137+
}
138+
139+
void llvm::memprof::addAmbiguousAttribute(CallBase *CB) {
140+
if (!MemProfUseAmbiguousAttributes)
141+
return;
142+
// We may have an existing ambiguous attribute if we are reanalyzing
143+
// after inlining.
144+
if (CB->hasFnAttr("memprof")) {
145+
assert(CB->getFnAttr("memprof").getValueAsString() == "ambiguous");
146+
} else {
147+
auto A = llvm::Attribute::get(CB->getContext(), "memprof", "ambiguous");
148+
CB->addFnAttr(A);
149+
}
150+
}
151+
128152
void CallStackTrie::addCallStack(
129153
AllocationType AllocType, ArrayRef<uint64_t> StackIds,
130154
std::vector<ContextTotalSize> ContextSizeInfo) {
@@ -470,6 +494,9 @@ void CallStackTrie::addSingleAllocTypeAttribute(CallBase *CI, AllocationType AT,
470494
StringRef Descriptor) {
471495
auto AllocTypeString = getAllocTypeAttributeString(AT);
472496
auto A = llvm::Attribute::get(CI->getContext(), "memprof", AllocTypeString);
497+
// After inlining we may be able to convert an existing ambiguous allocation
498+
// to an unambiguous one.
499+
removeAnyExistingAmbiguousAttribute(CI);
473500
CI->addFnAttr(A);
474501
if (MemProfReportHintedSizes) {
475502
std::vector<ContextTotalSize> ContextSizeInfo;
@@ -529,6 +556,7 @@ bool CallStackTrie::buildAndAttachMIBMetadata(CallBase *CI) {
529556
assert(MIBCallStack.size() == 1 &&
530557
"Should only be left with Alloc's location in stack");
531558
CI->setMetadata(LLVMContext::MD_memprof, MDNode::get(Ctx, MIBNodes));
559+
addAmbiguousAttribute(CI);
532560
return true;
533561
}
534562
// If there exists corner case that CallStackTrie has one chain to leaf

llvm/lib/Transforms/IPO/MemProfContextDisambiguation.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3986,6 +3986,7 @@ void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::identifyClones(
39863986
void ModuleCallsiteContextGraph::updateAllocationCall(
39873987
CallInfo &Call, AllocationType AllocType) {
39883988
std::string AllocTypeString = getAllocTypeAttributeString(AllocType);
3989+
removeAnyExistingAmbiguousAttribute(cast<CallBase>(Call.call()));
39893990
auto A = llvm::Attribute::get(Call.call()->getFunction()->getContext(),
39903991
"memprof", AllocTypeString);
39913992
cast<CallBase>(Call.call())->addFnAttr(A);
@@ -5661,9 +5662,10 @@ bool MemProfContextDisambiguation::applyImport(Module &M) {
56615662
auto *MemProfMD = I.getMetadata(LLVMContext::MD_memprof);
56625663

56635664
// Include allocs that were already assigned a memprof function
5664-
// attribute in the statistics.
5665-
if (CB->getAttributes().hasFnAttr("memprof")) {
5666-
assert(!MemProfMD);
5665+
// attribute in the statistics. Only do this for those that do not have
5666+
// memprof metadata, since we add an "ambiguous" memprof attribute by
5667+
// default.
5668+
if (CB->getAttributes().hasFnAttr("memprof") && !MemProfMD) {
56675669
CB->getAttributes().getFnAttr("memprof").getValueAsString() == "cold"
56685670
? AllocTypeColdThinBackend++
56695671
: AllocTypeNotColdThinBackend++;
@@ -5740,6 +5742,7 @@ bool MemProfContextDisambiguation::applyImport(Module &M) {
57405742
// clone J-1 (J==0 is the original clone and does not have a VMaps
57415743
// entry).
57425744
CBClone = cast<CallBase>((*VMaps[J - 1])[CB]);
5745+
removeAnyExistingAmbiguousAttribute(CBClone);
57435746
CBClone->addFnAttr(A);
57445747
ORE.emit(OptimizationRemark(DEBUG_TYPE, "MemprofAttribute", CBClone)
57455748
<< ore::NV("AllocationCall", CBClone) << " in clone "

llvm/test/ThinLTO/X86/memprof-basic.ll

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,9 @@ declare i32 @sleep()
103103

104104
define internal ptr @_Z3barv() #0 !dbg !15 {
105105
entry:
106-
%call = call ptr @_Znam(i64 0), !memprof !2, !callsite !7
106+
;; Use an ambiguous attribute for this allocation, which is now added to such
107+
;; allocations during matching. It should not affect cloning.
108+
%call = call ptr @_Znam(i64 0) #1, !memprof !2, !callsite !7
107109
ret ptr null
108110
}
109111

@@ -125,6 +127,7 @@ entry:
125127
uselistorder ptr @_Z3foov, { 1, 0 }
126128

127129
attributes #0 = { noinline optnone }
130+
attributes #1 = { "memprof"="ambiguous" }
128131

129132
!llvm.dbg.cu = !{!13}
130133
!llvm.module.flags = !{!20, !21}

llvm/test/Transforms/PGOProfile/memprof.ll

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
; ALL-NOT: no profile data available for function
3939

4040
;; Using a memprof-only profile for memprof-use should only give memprof metadata
41-
; RUN: opt < %s -passes='memprof-use<profile-filename=%t.memprofdata>' -pgo-warn-missing-function -S -memprof-print-match-info -stats 2>&1 | FileCheck %s --check-prefixes=MEMPROF,ALL,MEMPROFONLY,MEMPROFMATCHINFO,MEMPROFSTATS
41+
; RUN: opt < %s -passes='memprof-use<profile-filename=%t.memprofdata>' -pgo-warn-missing-function -S -memprof-print-match-info -stats 2>&1 | FileCheck %s --check-prefixes=MEMPROF,ALL,MEMPROFONLY,MEMPROFMATCHINFO,MEMPROFSTATS,AMBIG
4242
; There should not be any PGO metadata
4343
; MEMPROFONLY-NOT: !prof
4444

@@ -51,10 +51,10 @@
5151
;; Test the same thing but by passing the memory profile through to a default
5252
;; pipeline via -memory-profile-file=, which should cause the necessary field
5353
;; of the PGOOptions structure to be populated with the profile filename.
54-
; RUN: opt < %s -passes='default<O2>' -memory-profile-file=%t.memprofdata -pgo-warn-missing-function -S 2>&1 | FileCheck %s --check-prefixes=MEMPROF,ALL,MEMPROFONLY
54+
; RUN: opt < %s -passes='default<O2>' -memory-profile-file=%t.memprofdata -pgo-warn-missing-function -S 2>&1 | FileCheck %s --check-prefixes=MEMPROF,ALL,MEMPROFONLY,AMBIG
5555

5656
;; Using a pgo+memprof profile for memprof-use should only give memprof metadata
57-
; RUN: opt < %s -passes='memprof-use<profile-filename=%t.pgomemprofdata>' -pgo-warn-missing-function -S 2>&1 | FileCheck %s --check-prefixes=MEMPROF,ALL,MEMPROFONLY
57+
; RUN: opt < %s -passes='memprof-use<profile-filename=%t.pgomemprofdata>' -pgo-warn-missing-function -S 2>&1 | FileCheck %s --check-prefixes=MEMPROF,ALL,MEMPROFONLY,AMBIG
5858

5959
;; Using a pgo-only profile for memprof-use should give an error
6060
; RUN: not opt < %s -passes='memprof-use<profile-filename=%t.pgoprofdata>' -S 2>&1 | FileCheck %s --check-prefixes=MEMPROFWITHPGOONLY
@@ -72,7 +72,7 @@
7272

7373
;; Using a pgo+memprof profile for both memprof-use and pgo-instr-use should
7474
;; give both memprof and pgo metadata.
75-
; RUN: opt < %s -passes='pgo-instr-use,memprof-use<profile-filename=%t.pgomemprofdata>' -pgo-test-profile-file=%t.pgomemprofdata -pgo-warn-missing-function -S 2>&1 | FileCheck %s --check-prefixes=MEMPROF,ALL,PGO
75+
; RUN: opt < %s -passes='pgo-instr-use,memprof-use<profile-filename=%t.pgomemprofdata>' -pgo-test-profile-file=%t.pgomemprofdata -pgo-warn-missing-function -S 2>&1 | FileCheck %s --check-prefixes=MEMPROF,ALL,PGO,AMBIG
7676

7777
;; Check that the total sizes are reported if requested. A message should be
7878
;; emitted for the pruned context. Also check that remarks are emitted for the
@@ -108,7 +108,11 @@
108108

109109
;; However, with the same threshold, but hot hints not enabled, it should be
110110
;; notcold again.
111-
; RUN: opt < %s -passes='memprof-use<profile-filename=%t.memprofdata>' -pgo-warn-missing-function -S -memprof-min-ave-lifetime-access-density-hot-threshold=0 2>&1 | FileCheck %s --check-prefixes=MEMPROF,ALL
111+
; RUN: opt < %s -passes='memprof-use<profile-filename=%t.memprofdata>' -pgo-warn-missing-function -S -memprof-min-ave-lifetime-access-density-hot-threshold=0 2>&1 | FileCheck %s --check-prefixes=MEMPROF,ALL,AMBIG
112+
113+
;; Test that we don't get an ambiguous memprof attribute when
114+
;; -memprof-ambiguous-attributes is disabled.
115+
; RUN: opt < %s -passes='memprof-use<profile-filename=%t.memprofdata>' -pgo-warn-missing-function -S -memprof-ambiguous-attributes=false 2>&1 | FileCheck %s --check-prefixes=MEMPROF,ALL,NOAMBIG
112116

113117
; MEMPROFMATCHINFO: MemProf notcold context with id 1093248920606587996 has total profiled size 10 is matched with 1 frames
114118
; MEMPROFMATCHINFO: MemProf notcold context with id 5725971306423925017 has total profiled size 10 is matched with 1 frames
@@ -140,7 +144,7 @@ target triple = "x86_64-unknown-linux-gnu"
140144
; PGO: !prof
141145
define dso_local noundef ptr @_Z3foov() #0 !dbg !10 {
142146
entry:
143-
; MEMPROF: call {{.*}} @_Znam{{.*}} !memprof ![[M1:[0-9]+]], !callsite ![[C1:[0-9]+]]
147+
; MEMPROF: call {{.*}} @_Znam{{.*}} #[[A0:[0-9]+]]{{.*}} !memprof ![[M1:[0-9]+]], !callsite ![[C1:[0-9]+]]
144148
; MEMPROFNOCOLINFO: call {{.*}} @_Znam{{.*}} !memprof ![[M1:[0-9]+]], !callsite ![[C1:[0-9]+]]
145149
%call = call noalias noundef nonnull ptr @_Znam(i64 noundef 10) #6, !dbg !13
146150
ret ptr %call, !dbg !14
@@ -364,6 +368,9 @@ for.end: ; preds = %for.cond
364368
ret i32 0, !dbg !103
365369
}
366370

371+
;; We optionally apply an ambiguous memprof attribute to ambiguous allocations
372+
; AMBIG: #[[A0]] = { builtin allocsize(0) "memprof"="ambiguous" }
373+
; NOAMBIG: #[[A0]] = { builtin allocsize(0) }
367374
; MEMPROF: #[[A1]] = { builtin allocsize(0) "memprof"="notcold" }
368375
; MEMPROF: #[[A2]] = { builtin allocsize(0) "memprof"="cold" }
369376
; MEMPROF: ![[M1]] = !{![[MIB1:[0-9]+]], ![[MIB2:[0-9]+]], ![[MIB3:[0-9]+]], ![[MIB4:[0-9]+]]}

llvm/unittests/Analysis/MemoryProfileInfoTest.cpp

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,8 @@ declare dso_local noalias noundef i8* @malloc(i64 noundef)
230230
CallBase *Call = findCall(*Func, "call");
231231
Trie.buildAndAttachMIBMetadata(Call);
232232

233-
EXPECT_FALSE(Call->hasFnAttr("memprof"));
233+
EXPECT_TRUE(Call->hasFnAttr("memprof"));
234+
EXPECT_EQ(Call->getFnAttr("memprof").getValueAsString(), "ambiguous");
234235
EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
235236
MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
236237
ASSERT_EQ(MemProfMD->getNumOperands(), 2u);
@@ -279,7 +280,8 @@ declare dso_local noalias noundef i8* @malloc(i64 noundef)
279280
CallBase *Call = findCall(*Func, "call");
280281
Trie.buildAndAttachMIBMetadata(Call);
281282

282-
EXPECT_FALSE(Call->hasFnAttr("memprof"));
283+
EXPECT_TRUE(Call->hasFnAttr("memprof"));
284+
EXPECT_EQ(Call->getFnAttr("memprof").getValueAsString(), "ambiguous");
283285
EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
284286
MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
285287
ASSERT_EQ(MemProfMD->getNumOperands(), 2u);
@@ -333,7 +335,8 @@ declare dso_local noalias noundef i8* @malloc(i64 noundef)
333335
CallBase *Call = findCall(*Func, "call");
334336
Trie.buildAndAttachMIBMetadata(Call);
335337

336-
EXPECT_FALSE(Call->hasFnAttr("memprof"));
338+
EXPECT_TRUE(Call->hasFnAttr("memprof"));
339+
EXPECT_EQ(Call->getFnAttr("memprof").getValueAsString(), "ambiguous");
337340
EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
338341
MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
339342
ASSERT_EQ(MemProfMD->getNumOperands(), 2u);
@@ -392,7 +395,8 @@ declare dso_local noalias noundef i8* @malloc(i64 noundef)
392395
CallBase *Call = findCall(*Func, "call");
393396
Trie.buildAndAttachMIBMetadata(Call);
394397

395-
EXPECT_FALSE(Call->hasFnAttr("memprof"));
398+
EXPECT_TRUE(Call->hasFnAttr("memprof"));
399+
EXPECT_EQ(Call->getFnAttr("memprof").getValueAsString(), "ambiguous");
396400
EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
397401
MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
398402
ASSERT_EQ(MemProfMD->getNumOperands(), 2u);
@@ -463,7 +467,8 @@ declare dso_local noalias noundef i8* @malloc(i64 noundef)
463467
ASSERT_NE(Call, nullptr);
464468
Trie.buildAndAttachMIBMetadata(Call);
465469

466-
EXPECT_FALSE(Call->hasFnAttr("memprof"));
470+
EXPECT_TRUE(Call->hasFnAttr("memprof"));
471+
EXPECT_EQ(Call->getFnAttr("memprof").getValueAsString(), "ambiguous");
467472
EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
468473
MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
469474
EXPECT_THAT(MemProfMD, MemprofMetadataEquals(ExpectedVals));
@@ -536,7 +541,8 @@ declare dso_local noalias noundef i8* @malloc(i64 noundef)
536541
// Restore original option value.
537542
MemProfKeepAllNotColdContexts = OrigMemProfKeepAllNotColdContexts;
538543

539-
EXPECT_FALSE(Call->hasFnAttr("memprof"));
544+
EXPECT_TRUE(Call->hasFnAttr("memprof"));
545+
EXPECT_EQ(Call->getFnAttr("memprof").getValueAsString(), "ambiguous");
540546
EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
541547
MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
542548
EXPECT_THAT(MemProfMD, MemprofMetadataEquals(ExpectedVals));
@@ -664,7 +670,8 @@ declare dso_local noalias noundef i8* @malloc(i64 noundef)
664670
// The hot allocations will be converted to NotCold and pruned as they
665671
// are unnecessary to determine how to clone the cold allocation.
666672

667-
EXPECT_FALSE(Call->hasFnAttr("memprof"));
673+
EXPECT_TRUE(Call->hasFnAttr("memprof"));
674+
EXPECT_EQ(Call->getFnAttr("memprof").getValueAsString(), "ambiguous");
668675
EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
669676
MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
670677
ASSERT_EQ(MemProfMD->getNumOperands(), 2u);

0 commit comments

Comments
 (0)