Index: llvm/include/llvm/ProfileData/SampleProfReader.h =================================================================== --- llvm/include/llvm/ProfileData/SampleProfReader.h +++ llvm/include/llvm/ProfileData/SampleProfReader.h @@ -235,6 +235,8 @@ namespace sampleprof { +class SampleProfileReaderItaniumRemapper; + /// Sample-based profile reader. /// /// Each profile contains sample counts for all the functions @@ -335,6 +337,8 @@ virtual std::vector *getNameTable() { return nullptr; } virtual bool dumpSectionInfo(raw_ostream &OS = dbgs()) { return false; }; + void setRemapper(SampleProfileReaderItaniumRemapper *R) { Remapper = R; } + protected: /// Map every function to its associated profile. /// @@ -361,6 +365,8 @@ /// Compute summary for this profile. void computeSummary(); + SampleProfileReaderItaniumRemapper *Remapper = nullptr; + /// \brief The format of sample. SampleProfileFormat Format = SPF_None; }; @@ -645,10 +651,14 @@ /// between components of the symbol names in the profile. class SampleProfileReaderItaniumRemapper : public SampleProfileReader { public: - SampleProfileReaderItaniumRemapper( - std::unique_ptr B, LLVMContext &C, - std::unique_ptr Underlying) - : SampleProfileReader(std::move(B), C, Underlying->getFormat()) { + SampleProfileReaderItaniumRemapper(std::unique_ptr R, + std::unique_ptr B, + LLVMContext &C) + : SampleProfileReader(std::move(B), C, SPF_None), + Remappings(std::move(R)) {} + + void setUnderlyingReader(std::unique_ptr Underlying) { + Format = Underlying->getFormat(); Profiles = std::move(Underlying->getProfiles()); Summary = takeSummary(*Underlying); // Keep the underlying reader alive; the profile data may contain @@ -656,24 +666,35 @@ UnderlyingReader = std::move(Underlying); } - /// Create a remapped sample profile from the given remapping file and - /// underlying samples. - static ErrorOr> - create(const Twine &Filename, LLVMContext &C, - std::unique_ptr Underlying); + /// Create a remapped sample profile from the given remapping file. + static ErrorOr> + create(const Twine &Filename, LLVMContext &C); + + /// Create a remapped sample profile from the given Buffer. + static ErrorOr> + create(std::unique_ptr B, LLVMContext &C); /// Read and validate the file header. std::error_code readHeader() override { return sampleprof_error::success; } - /// Read remapping file and apply it to the sample profile. + /// Apply remappings to the sample profile. std::error_code read() override; + /// Insert function name into remapper. + void insert(StringRef FunctionName) { Remappings->insert(FunctionName); } + + /// Query whether there is equivalent in the remapper which has been + /// inserted. + bool exist(StringRef FunctionName) { + return Remappings->lookup(FunctionName); + } + /// Return the samples collected for function \p F. FunctionSamples *getSamplesFor(StringRef FunctionName) override; using SampleProfileReader::getSamplesFor; private: - SymbolRemappingReader Remappings; + std::unique_ptr Remappings; DenseMap SampleMap; std::unique_ptr UnderlyingReader; }; Index: llvm/lib/ProfileData/SampleProfReader.cpp =================================================================== --- llvm/lib/ProfileData/SampleProfReader.cpp +++ llvm/lib/ProfileData/SampleProfReader.cpp @@ -540,15 +540,23 @@ return sampleprof_error::success; } - for (auto Name : FuncsToUse) { - auto iter = FuncOffsetTable.find(Name); - if (iter == FuncOffsetTable.end()) + if (Remapper) { + for (auto Name : FuncsToUse) { + Remapper->insert(Name); + } + } + + for (auto NameOffset : FuncOffsetTable) { + auto FuncName = NameOffset.first; + if (!FuncsToUse.count(FuncName) && + (!Remapper || !Remapper->exist(FuncName))) continue; - const uint8_t *FuncProfileAddr = Start + iter->second; + const uint8_t *FuncProfileAddr = Start + NameOffset.second; assert(FuncProfileAddr < End && "out of LBRProfile section"); if (std::error_code EC = readFuncProfile(FuncProfileAddr)) return EC; } + Data = End; return sampleprof_error::success; } @@ -1202,6 +1210,9 @@ } std::error_code SampleProfileReaderItaniumRemapper::read() { + if (getFormat() == SPF_None) + llvm_unreachable("UnderlyingReader must be setup before remapper reads"); + // If the underlying data is in compact format, we can't remap it because // we don't know what the original function names were. if (getFormat() == SPF_Compact_Binary) { @@ -1213,16 +1224,8 @@ return sampleprof_error::success; } - if (Error E = Remappings.read(*Buffer)) { - handleAllErrors( - std::move(E), [&](const SymbolRemappingParseError &ParseError) { - reportError(ParseError.getLineNum(), ParseError.getMessage()); - }); - return sampleprof_error::malformed; - } - for (auto &Sample : getProfiles()) - if (auto Key = Remappings.insert(Sample.first())) + if (auto Key = Remappings->insert(Sample.first())) SampleMap.insert({Key, &Sample.second}); return sampleprof_error::success; @@ -1230,7 +1233,7 @@ FunctionSamples * SampleProfileReaderItaniumRemapper::getSamplesFor(StringRef Fname) { - if (auto Key = Remappings.lookup(Fname)) + if (auto Key = Remappings->lookup(Fname)) return SampleMap.lookup(Key); return SampleProfileReader::getSamplesFor(Fname); } @@ -1274,18 +1277,39 @@ /// /// \param C The LLVM context to use to emit diagnostics. /// -/// \param Underlying The underlying profile data reader to remap. -/// /// \returns an error code indicating the status of the created reader. -ErrorOr> -SampleProfileReaderItaniumRemapper::create( - const Twine &Filename, LLVMContext &C, - std::unique_ptr Underlying) { +ErrorOr> +SampleProfileReaderItaniumRemapper::create(const Twine &Filename, + LLVMContext &C) { auto BufferOrError = setupMemoryBuffer(Filename); if (std::error_code EC = BufferOrError.getError()) return EC; + return create(std::move(BufferOrError.get()), C); +} + +/// Create a sample profile remapper from the given input, to remap the +/// function names in the given profile data. +/// +/// \param B The memory buffer to create the reader from (assumes ownership). +/// +/// \param C The LLVM context to use to emit diagnostics. +/// +/// \returns an error code indicating the status of the created reader. +ErrorOr> +SampleProfileReaderItaniumRemapper::create(std::unique_ptr B, + LLVMContext &C) { + auto Remappings = std::make_unique(); + if (Error E = Remappings->read(*B.get())) { + handleAllErrors( + std::move(E), [&](const SymbolRemappingParseError &ParseError) { + C.diagnose(DiagnosticInfoSampleProfile(B->getBufferIdentifier(), + ParseError.getLineNum(), + ParseError.getMessage())); + }); + return sampleprof_error::malformed; + } return std::make_unique( - std::move(BufferOrError.get()), C, std::move(Underlying)); + std::move(Remappings), std::move(B), C); } /// Create a sample profile reader based on the format of the input data. Index: llvm/lib/Transforms/IPO/SampleProfile.cpp =================================================================== --- llvm/lib/Transforms/IPO/SampleProfile.cpp +++ llvm/lib/Transforms/IPO/SampleProfile.cpp @@ -1675,6 +1675,23 @@ bool SampleProfileLoader::doInitialization(Module &M) { auto &Ctx = M.getContext(); + + std::unique_ptr RemapReader; + if (!RemappingFilename.empty()) { + // Apply profile remappings to the loaded profile data if requested. + // For now, we only support remapping symbols encoded using the Itanium + // C++ ABI's name mangling scheme. + auto ReaderOrErr = + SampleProfileReaderItaniumRemapper::create(RemappingFilename, Ctx); + if (std::error_code EC = ReaderOrErr.getError()) { + std::string Msg = + "Could not read profile remapping file: " + EC.message(); + Ctx.diagnose(DiagnosticInfoSampleProfile(Filename, Msg)); + return false; + } + RemapReader = std::move(ReaderOrErr.get()); + } + auto ReaderOrErr = SampleProfileReader::create(Filename, Ctx); if (std::error_code EC = ReaderOrErr.getError()) { std::string Msg = "Could not open profile: " + EC.message(); @@ -1683,6 +1700,9 @@ } Reader = std::move(ReaderOrErr.get()); Reader->collectFuncsFrom(M); + // Set up Remapper before read because on demand profile loading may need + // to compare name in the module with name in the profile using Remapper. + Reader->setRemapper(RemapReader.get()); ProfileIsValid = (Reader->read() == sampleprof_error::success); PSL = Reader->getProfileSymbolList(); @@ -1695,18 +1715,9 @@ NamesInProfile.insert(NameTable->begin(), NameTable->end()); } - if (!RemappingFilename.empty()) { - // Apply profile remappings to the loaded profile data if requested. - // For now, we only support remapping symbols encoded using the Itanium - // C++ ABI's name mangling scheme. - ReaderOrErr = SampleProfileReaderItaniumRemapper::create( - RemappingFilename, Ctx, std::move(Reader)); - if (std::error_code EC = ReaderOrErr.getError()) { - std::string Msg = "Could not open profile remapping file: " + EC.message(); - Ctx.diagnose(DiagnosticInfoSampleProfile(Filename, Msg)); - return false; - } - Reader = std::move(ReaderOrErr.get()); + if (RemapReader) { + RemapReader->setUnderlyingReader(std::move(Reader)); + Reader = std::move(RemapReader); ProfileIsValid = (Reader->read() == sampleprof_error::success); } return true; Index: llvm/test/Transforms/SampleProfile/remap.ll =================================================================== --- llvm/test/Transforms/SampleProfile/remap.ll +++ llvm/test/Transforms/SampleProfile/remap.ll @@ -1,5 +1,9 @@ ; RUN: opt %s -passes=sample-profile -sample-profile-file=%S/Inputs/remap.prof -sample-profile-remapping-file=%S/Inputs/remap.map | opt -analyze -branch-prob | FileCheck %s - +; +; Check whether profile remapping work with loading profile on demand used by extbinary format profile. +; RUN: llvm-profdata merge -sample -extbinary %S/Inputs/remap.prof -o %t.extbinary.afdo +; RUN: opt %s -passes=sample-profile -sample-profile-file=%S/Inputs/remap.prof -sample-profile-remapping-file=%S/Inputs/remap.map | opt -analyze -branch-prob | FileCheck %s +; ; Reduced from branch.ll declare i1 @foo() Index: llvm/unittests/ProfileData/SampleProfTest.cpp =================================================================== --- llvm/unittests/ProfileData/SampleProfTest.cpp +++ llvm/unittests/ProfileData/SampleProfTest.cpp @@ -93,16 +93,43 @@ BazSamples.addHeadSamples(1257); BazSamples.addBodySamples(1, 0, 12557); - Module M("my_module", Context); - FunctionType *fn_type = - FunctionType::get(Type::getVoidTy(Context), {}, false); - M.getOrInsertFunction(FooName, fn_type); - M.getOrInsertFunction(BarName, fn_type); + StringRef BooName("_Z3booi"); + FunctionSamples BooSamples; + BooSamples.setName(BooName); + BooSamples.addTotalSamples(1232); + BooSamples.addHeadSamples(1); + BooSamples.addBodySamples(1, 0, 1232); StringMap Profiles; Profiles[FooName] = std::move(FooSamples); Profiles[BarName] = std::move(BarSamples); Profiles[BazName] = std::move(BazSamples); + Profiles[BooName] = std::move(BooSamples); + + Module M("my_module", Context); + FunctionType *fn_type = + FunctionType::get(Type::getVoidTy(Context), {}, false); + + std::unique_ptr RemapReader; + if (Remap) { + auto MemBuffer = llvm::MemoryBuffer::getMemBuffer(R"( + # Types 'int' and 'long' are equivalent + type i l + # Function names 'foo' and 'faux' are equivalent + name 3foo 4faux + )"); + auto ReaderOrErr = SampleProfileReaderItaniumRemapper::create( + std::move(MemBuffer), Context); + ASSERT_TRUE(NoError(ReaderOrErr.getError())); + RemapReader = std::move(ReaderOrErr.get()); + + FooName = "_Z4fauxi"; + BarName = "_Z3barl"; + } + + M.getOrInsertFunction(FooName, fn_type); + M.getOrInsertFunction(BarName, fn_type); + M.getOrInsertFunction(BooName, fn_type); ProfileSymbolList List; if (Format == SampleProfileFormat::SPF_Ext_Binary) { @@ -118,7 +145,9 @@ Writer->getOutputStream().flush(); readProfile(M, Profile); - + // Set up Remapper before read because on demand profile loading may need + // to compare name in the module with name in the profile using Remapper. + Reader->setRemapper(RemapReader.get()); EC = Reader->read(); ASSERT_TRUE(NoError(EC)); @@ -130,16 +159,8 @@ } if (Remap) { - auto MemBuffer = llvm::MemoryBuffer::getMemBuffer(R"( - # Types 'int' and 'long' are equivalent - type i l - # Function names 'foo' and 'faux' are equivalent - name 3foo 4faux - )"); - Reader.reset(new SampleProfileReaderItaniumRemapper( - std::move(MemBuffer), Context, std::move(Reader))); - FooName = "_Z4fauxi"; - BarName = "_Z3barl"; + RemapReader->setUnderlyingReader(std::move(Reader)); + Reader = std::move(RemapReader); EC = Reader->read(); ASSERT_TRUE(NoError(EC)); @@ -171,13 +192,17 @@ if (Format == SampleProfileFormat::SPF_Ext_Binary || Format == SampleProfileFormat::SPF_Compact_Binary) { ASSERT_TRUE(ReadBazSamples == nullptr); - ASSERT_EQ(2u, Reader->getProfiles().size()); + ASSERT_EQ(3u, Reader->getProfiles().size()); } else { ASSERT_TRUE(ReadBazSamples != nullptr); ASSERT_EQ(12557u, ReadBazSamples->getTotalSamples()); - ASSERT_EQ(3u, Reader->getProfiles().size()); + ASSERT_EQ(4u, Reader->getProfiles().size()); } + FunctionSamples *ReadBooSamples = Reader->getSamplesFor(BooName); + ASSERT_TRUE(ReadBooSamples != nullptr); + ASSERT_EQ(1232u, ReadBooSamples->getTotalSamples()); + std::string MconstructGUID; StringRef MconstructRep = getRepInFormat(MconstructName, Format, MconstructGUID); @@ -189,9 +214,9 @@ auto VerifySummary = [](ProfileSummary &Summary) mutable { ASSERT_EQ(ProfileSummary::PSK_Sample, Summary.getKind()); - ASSERT_EQ(136160u, Summary.getTotalCount()); - ASSERT_EQ(7u, Summary.getNumCounts()); - ASSERT_EQ(3u, Summary.getNumFunctions()); + ASSERT_EQ(137392u, Summary.getTotalCount()); + ASSERT_EQ(8u, Summary.getNumCounts()); + ASSERT_EQ(4u, Summary.getNumFunctions()); ASSERT_EQ(1437u, Summary.getMaxFunctionCount()); ASSERT_EQ(60351u, Summary.getMaxCount());