This is an archive of the discontinued LLVM Phabricator instance.

[SampleProfile] Potential use after move in SampleProfileLoader::promoteMergeNotInlinedContextSamples
ClosedPublic

Authored by huangjd on Aug 3 2023, 5:50 PM.

Details

Summary

SampleProfileLoader::promoteMergeNotInlinedContextSample adds
certain uninlined functions to the sample profile map (unordered_map, which is
previously read from a profile file). This action may cause the map to
be rehashed, invalidating all pointers to FunctionSamples used by many
members of SampleProfileLoader, while the existing code did nothing to
guard against that. This bug is theoretical since adding a few
new functions to a large profile usually won't trigger a rehash, or even
if there's a rehash std::unordered_map tries its best to expand its
capacity in-place.

This bug will trigger if the container type of sample profile map is
changed to llvm::DenseMap or other implementation, such as in D147740,
for SampleProfReader's performance reason.

Diff Detail

Event Timeline

huangjd created this revision.Aug 3 2023, 5:50 PM
Herald added a project: Restricted Project. · View Herald TranscriptAug 3 2023, 5:50 PM
huangjd requested review of this revision.Aug 3 2023, 5:50 PM
Herald added a project: Restricted Project. · View Herald TranscriptAug 3 2023, 5:50 PM
davidxl added inline comments.Aug 3 2023, 10:06 PM
llvm/lib/Transforms/IPO/SampleProfile.cpp
1638

is it enough to just use a flag to indicate insertion happens (instead of using a vector)? The getOrCreateSamples can set the flag.

1646

insertion does not mean rehashing happens. Is there a better way to avoid the unncessary update?

huangjd added inline comments.Aug 4 2023, 9:05 AM
llvm/lib/Transforms/IPO/SampleProfile.cpp
1638

The vector also stores a deep copy of the caller's FunctionSamples that's to be merged with newly created outlined function's FunctionSamples. Note that NonInlinedCallSites uses pointers to existing FunctionSamples, which will be invalidated if relocation happens, so we need a copy instead.

1646

C++ unordered_map or llvm::DenseMap does not provide an interface to check if data are relocated. There's ugly way to check it such as comparing the address to the first element. However, rehashing is likely to happen if insertion happens, because the profile reader reserves the exact number of FunctionSamples in the map. Meanwhile insertion itself is unlikely to happen as this function is called very few times in a large production project, so it wouldn't matter for performance.

davidxl added inline comments.Aug 4 2023, 9:28 AM
llvm/lib/Transforms/IPO/SampleProfile.cpp
1638

Ok. Can you modify the comments to reflect this: avoid creating new function samples (for outline function) in the the loop, otherwise the pointers used in the loop iteration can be invalidated.

1646

make sense. Can you also update the comment saying this is rare event (i.e., missing function samples for outline function).

wlei added inline comments.Aug 4 2023, 11:11 AM
llvm/lib/Transforms/IPO/SampleProfile.cpp
1658–1661

Curious why we need to reset the SampleProfileMatcher?

huangjd updated this revision to Diff 547434.Aug 4 2023, 6:49 PM

Updated comment, remove unnecessary reset of SampleProfileMatcher

llvm/lib/Transforms/IPO/SampleProfile.cpp
1658–1661

Removed. This field does not need to be updated

wlei added inline comments.Aug 8 2023, 12:06 PM
llvm/lib/Transforms/IPO/SampleProfile.cpp
1656

It looks like promoteMergeNotInlinedContextSamples are not for CS profile, see the comments:

// For CS profile, profile for not inlined context will be merged when
// base profile is being retrieved.
if (!FunctionSamples::ProfileIsCS)
  promoteMergeNotInlinedContextSamples(LocalNotInlinedCallSites, F);

probably just assert on !Reader->profileIsCS()

wenlei added inline comments.Aug 8 2023, 4:21 PM
llvm/lib/Transforms/IPO/SampleProfile.cpp
1647–1649

Patching all pointers seems a bit hacky. Have you considered maintaining reference stability by using a separate container for new function samples, so there will be no insertion to SampleProfileReader::Profiles?

Strictly speaking SampleProfileReader::Profiles should only contain profile directly read out of input, and those newly created profiles are synthesized which doesn't belong to reader.

If we use a separate container (say SynthesizedFuncSamples), we would only need to change the code below to look for profiles from the addition container as well for non-CS profile.

if (FunctionSamples::ProfileIsCS)
  Samples = ContextTracker->getBaseSamplesFor(F);
else
  Samples = Reader->getSamplesFor(F);

-->

if (FunctionSamples::ProfileIsCS)
  Samples = ContextTracker->getBaseSamplesFor(F);
else {
  Samples = Reader->getSamplesFor(F);
  if (!Samples)
    Samples = findSynthesizedProfile(F);
}
1655–1656

Yeah we should assert this is non CS profile. CS profile doesn't need promoteMergeNotInlinedContextSamples as all adjustments are done in SampleContextTracker without creating new FunctionSamples.

1657–1658

This is super heavy hammer to rebuild all context trie, which would have been a deal breaker. But again, we shouldn't need any of this for CS profile.

huangjd updated this revision to Diff 549583.Aug 12 2023, 1:24 AM

Rewrite patch, use a much cleaner approach by separating the sample profile map

wenlei added inline comments.Aug 14 2023, 3:39 PM
llvm/include/llvm/ProfileData/SampleProfReader.h
510

Can this be owned by SampleProfileLoader instead? Reader should just present what's being read out of an input profile, and should not couple with profile loader in other ways.

huangjd updated this revision to Diff 550554.Aug 15 2023, 5:01 PM

Update to make SampleProfileMatcher own the outline profiles

wenlei added inline comments.Aug 15 2023, 5:20 PM
llvm/lib/Transforms/IPO/SampleProfile.cpp
1622

We can actually remove the getOrCreateSamplesFor API from reader now to guarantee it's immutable after profile is read.

1626

We could also be inserting into OutlineFunctionSamples which can cause rehash while using a FunctionSamples from OutlineFunctionSamples?

I'm wondering if OutlineFunctionSamples should use a container that guarantees reference stability, e.g. std::map instead of std::unordered_map.

huangjd added inline comments.Aug 15 2023, 5:38 PM
llvm/lib/Transforms/IPO/SampleProfile.cpp
1626

That's OK, there is no pointer reference OutlineFunctionSamples, so there's no concern of segfault. Performance is trivial because very few inlined functions are actually promoted to outline function (say < 10 in a production project )

huangjd updated this revision to Diff 550560.Aug 15 2023, 5:40 PM

Remove unused getOrCreateSamples function

llvm/lib/Transforms/IPO/SampleProfile.cpp
1626

The pointer here is used immediately and won't be used outside of the function

huangjd marked an inline comment as done.Aug 15 2023, 5:41 PM
wenlei added inline comments.Aug 15 2023, 8:52 PM
llvm/lib/Transforms/IPO/SampleProfile.cpp
1626

That's OK, there is no pointer reference OutlineFunctionSamples, so there's no concern of segfault.

Imagine this case: we are processing a function (inside SampleProfileLoader::runOnFunction) whose own profile is retrieved from OutlineFunctionSamples, now its callee profile got inserted into OutlineFunctionSamples, causing a rehash, so SampleProfileLoader::Samples now becomes a dangling reference,

Performance is trivial because very few inlined functions are actually promoted to outline function (say < 10 in a production project )

Right, so we can afford to use std::map.

huangjd updated this revision to Diff 550852.Aug 16 2023, 1:07 PM

use map instead of unordered_map

wenlei accepted this revision.Aug 16 2023, 1:16 PM

thanks for the fix and following up with the comments, lgtm now.

llvm/include/llvm/Transforms/Utils/SampleProfileLoaderBaseImpl.h
270

Add a comment to explain why we use std::map here instead of existing SampleProfileMap, in case others stomped on this code later and tempted to change it to SampleProfileMap for consistency.

This revision is now accepted and ready to land.Aug 16 2023, 1:16 PM
huangjd marked an inline comment as done.Aug 16 2023, 1:16 PM
huangjd updated this revision to Diff 550857.Aug 16 2023, 1:21 PM

Added comment

This revision was landed with ongoing or failed builds.Aug 16 2023, 1:32 PM
This revision was automatically updated to reflect the committed changes.
This comment was removed by ayermolo.