diff --git a/bolt/include/bolt/Profile/ProfileYAMLMapping.h b/bolt/include/bolt/Profile/ProfileYAMLMapping.h --- a/bolt/include/bolt/Profile/ProfileYAMLMapping.h +++ b/bolt/include/bolt/Profile/ProfileYAMLMapping.h @@ -179,7 +179,7 @@ namespace bolt { struct BinaryProfileHeader { - uint32_t Version{1}; + uint32_t Version{2}; std::string FileName; // Name of the profiled binary. std::string Id; // BuildID. PROFILE_PF Flags{BinaryFunction::PF_NONE}; @@ -225,4 +225,6 @@ } // end namespace yaml } // end namespace llvm +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::yaml::bolt::BinaryProfile) + #endif diff --git a/bolt/include/bolt/Profile/YAMLProfileWriter.h b/bolt/include/bolt/Profile/YAMLProfileWriter.h --- a/bolt/include/bolt/Profile/YAMLProfileWriter.h +++ b/bolt/include/bolt/Profile/YAMLProfileWriter.h @@ -15,6 +15,7 @@ namespace llvm { namespace bolt { class RewriteInstance; +class ProfileReaderBase; class YAMLProfileWriter { YAMLProfileWriter() = delete; @@ -28,7 +29,9 @@ : Filename(Filename) {} /// Save execution profile for that instance. - std::error_code writeProfile(const RewriteInstance &RI); + std::error_code + writeProfile(const RewriteInstance &RI, + std::vector> &ProfileReaders); }; } // namespace bolt diff --git a/bolt/include/bolt/Rewrite/RewriteInstance.h b/bolt/include/bolt/Rewrite/RewriteInstance.h --- a/bolt/include/bolt/Rewrite/RewriteInstance.h +++ b/bolt/include/bolt/Rewrite/RewriteInstance.h @@ -83,11 +83,6 @@ /// otherwise. std::optional getPrintableBuildID() const; - /// If this instance uses a profile, return appropriate profile reader. - const ProfileReaderBase *getProfileReader() const { - return ProfileReader.get(); - } - private: using ELF64LEPhdrTy = object::ELF64LEFile::Elf_Phdr; @@ -157,6 +152,7 @@ void readDebugInfo(); /// Read profile data without having disassembled functions available. + void preprocessProfile(std::unique_ptr &ProfileReader); void preprocessProfileData(); void processProfileDataPreCFG(); @@ -449,7 +445,7 @@ const char *const *Argv; StringRef ToolPath; - std::unique_ptr ProfileReader; + std::vector> ProfileReaders; std::unique_ptr BC; std::unique_ptr CFIRdWrt; diff --git a/bolt/include/bolt/Utils/CommandLineOpts.h b/bolt/include/bolt/Utils/CommandLineOpts.h --- a/bolt/include/bolt/Utils/CommandLineOpts.h +++ b/bolt/include/bolt/Utils/CommandLineOpts.h @@ -47,7 +47,7 @@ extern llvm::cl::opt Hugify; extern llvm::cl::opt Instrument; extern llvm::cl::opt OutputFilename; -extern llvm::cl::opt PerfData; +extern llvm::cl::list PerfData; extern llvm::cl::opt PrintCacheMetrics; extern llvm::cl::opt PrintSections; diff --git a/bolt/lib/Profile/YAMLProfileWriter.cpp b/bolt/lib/Profile/YAMLProfileWriter.cpp --- a/bolt/lib/Profile/YAMLProfileWriter.cpp +++ b/bolt/lib/Profile/YAMLProfileWriter.cpp @@ -137,7 +137,9 @@ } } // end anonymous namespace -std::error_code YAMLProfileWriter::writeProfile(const RewriteInstance &RI) { +std::error_code YAMLProfileWriter::writeProfile( + const RewriteInstance &RI, + std::vector> &ProfileReaders) { const BinaryContext &BC = RI.getBinaryContext(); const auto &Functions = BC.getBinaryFunctions(); @@ -149,55 +151,59 @@ return EC; } - yaml::bolt::BinaryProfile BP; - - // Fill out the header info. - BP.Header.Version = 1; - BP.Header.FileName = std::string(BC.getFilename()); - std::optional BuildID = BC.getFileBuildID(); - BP.Header.Id = BuildID ? std::string(*BuildID) : ""; - BP.Header.Origin = std::string(RI.getProfileReader()->getReaderName()); - - StringSet<> EventNames = RI.getProfileReader()->getEventNames(); - if (!EventNames.empty()) { - std::string Sep; - for (const StringMapEntry &EventEntry : EventNames) { - BP.Header.EventNames += Sep + EventEntry.first().str(); - Sep = ","; + std::vector BPs; + for (std::unique_ptr &ProfileReader : ProfileReaders) { + yaml::bolt::BinaryProfile BP; + + // Fill out the header info. + BP.Header.Version = 2; + BP.Header.FileName = std::string(BC.getFilename()); + std::optional BuildID = BC.getFileBuildID(); + BP.Header.Id = BuildID ? std::string(*BuildID) : ""; + BP.Header.Origin = std::string(ProfileReader->getReaderName()); + + StringSet<> EventNames = ProfileReader->getEventNames(); + if (!EventNames.empty()) { + std::string Sep; + for (const StringMapEntry &EventEntry : EventNames) { + BP.Header.EventNames += Sep + EventEntry.first().str(); + Sep = ","; + } } - } - // Make sure the profile is consistent across all functions. - uint16_t ProfileFlags = BinaryFunction::PF_NONE; - for (const auto &BFI : Functions) { - const BinaryFunction &BF = BFI.second; - if (BF.hasProfile() && !BF.empty()) { - assert(BF.getProfileFlags() != BinaryFunction::PF_NONE); - if (ProfileFlags == BinaryFunction::PF_NONE) - ProfileFlags = BF.getProfileFlags(); - - assert(BF.getProfileFlags() == ProfileFlags && - "expected consistent profile flags across all functions"); + // Make sure the profile is consistent across all functions. + uint16_t ProfileFlags = BinaryFunction::PF_NONE; + for (const auto &BFI : Functions) { + const BinaryFunction &BF = BFI.second; + if (BF.hasProfile() && !BF.empty()) { + assert(BF.getProfileFlags() != BinaryFunction::PF_NONE); + if (ProfileFlags == BinaryFunction::PF_NONE) + ProfileFlags = BF.getProfileFlags(); + + assert(BF.getProfileFlags() == ProfileFlags && + "expected consistent profile flags across all functions"); + } } - } - BP.Header.Flags = ProfileFlags; + BP.Header.Flags = ProfileFlags; - // Add all function objects. - for (const auto &BFI : Functions) { - const BinaryFunction &BF = BFI.second; - if (BF.hasProfile()) { - if (!BF.hasValidProfile() && !RI.getProfileReader()->isTrustedSource()) - continue; + // Add all function objects. + for (const auto &BFI : Functions) { + const BinaryFunction &BF = BFI.second; + if (BF.hasProfile()) { + if (!BF.hasValidProfile() && !ProfileReader->isTrustedSource()) + continue; - yaml::bolt::BinaryFunctionProfile YamlBF; - convert(BF, YamlBF); - BP.Functions.emplace_back(YamlBF); + yaml::bolt::BinaryFunctionProfile YamlBF; + convert(BF, YamlBF); + BP.Functions.emplace_back(YamlBF); + } } + BPs.push_back(BP); } // Write the profile. yaml::Output Out(*OS, nullptr, 0); - Out << BP; + Out << BPs; return std::error_code(); } diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -377,14 +377,7 @@ if (!sys::fs::exists(Filename)) return errorCodeToError(make_error_code(errc::no_such_file_or_directory)); - if (ProfileReader) { - // Already exists - return make_error(Twine("multiple profiles specified: ") + - ProfileReader->getFilename() + " and " + - Filename, - inconvertibleErrorCode()); - } - + std::unique_ptr ProfileReader; // Spawn a profile reader based on file contents. if (DataAggregator::checkPerfDataMagic(Filename)) ProfileReader = std::make_unique(Filename); @@ -393,6 +386,8 @@ else ProfileReader = std::make_unique(Filename); + ProfileReaders.emplace_back(std::move(ProfileReader)); + return Error::success(); } @@ -1795,7 +1790,7 @@ } if (BC->isX86() && BC->HasRelocations && - opts::AlignMacroOpFusion == MFT_HOT && !ProfileReader) { + opts::AlignMacroOpFusion == MFT_HOT && ProfileReaders.empty()) { outs() << "BOLT-INFO: enabling -align-macro-fusion=all since no profile " "was specified\n"; opts::AlignMacroOpFusion = MFT_ALL; @@ -2769,6 +2764,15 @@ } } +static bool +hasProfile(std::vector> &ProfileReaders, + const BinaryFunction &BF) { + return llvm::any_of(ProfileReaders, + [&](std::unique_ptr &PR) { + return PR->mayHaveProfileData(BF); + }); +} + void RewriteInstance::selectFunctionsToProcess() { // Extend the list of functions to process or skip from a file. auto populateFunctionNames = [](cl::opt &FunctionNamesFile, @@ -2804,7 +2808,7 @@ std::vector TopFunctions; for (auto &BFI : BC->getBinaryFunctions()) { const BinaryFunction &Function = BFI.second; - if (ProfileReader->mayHaveProfileData(Function)) + if (hasProfile(ProfileReaders, Function)) TopFunctions.push_back(&Function); } llvm::sort( @@ -2878,7 +2882,7 @@ return true; } - if (ProfileReader && !ProfileReader->mayHaveProfileData(Function)) + if (!hasProfile(ProfileReaders, Function)) return false; if (Function.getKnownExecutionCount() < LiteThresholdExecCount) @@ -2972,12 +2976,15 @@ } void RewriteInstance::preprocessProfileData() { - if (!ProfileReader) - return; - NamedRegionTimer T("preprocessprofile", "pre-process profile data", TimerGroupName, TimerGroupDesc, opts::TimeRewrite); + for (std::unique_ptr &PR : ProfileReaders) + preprocessProfile(PR); +} + +void RewriteInstance::preprocessProfile( + std::unique_ptr &ProfileReader) { outs() << "BOLT-INFO: pre-processing profile using " << ProfileReader->getReaderName() << '\n'; @@ -3000,38 +3007,36 @@ } void RewriteInstance::processProfileDataPreCFG() { - if (!ProfileReader) - return; - NamedRegionTimer T("processprofile-precfg", "process profile data pre-CFG", TimerGroupName, TimerGroupDesc, opts::TimeRewrite); - if (Error E = ProfileReader->readProfilePreCFG(*BC.get())) - report_error("cannot read profile pre-CFG", std::move(E)); + for (std::unique_ptr &PR : ProfileReaders) + if (Error E = PR->readProfilePreCFG(*BC.get())) + report_error("cannot read profile pre-CFG", std::move(E)); } void RewriteInstance::processProfileData() { - if (!ProfileReader) - return; - NamedRegionTimer T("processprofile", "process profile data", TimerGroupName, TimerGroupDesc, opts::TimeRewrite); - if (Error E = ProfileReader->readProfile(*BC.get())) - report_error("cannot read profile", std::move(E)); + for (std::unique_ptr &PR : ProfileReaders) + if (Error E = PR->readProfile(*BC.get())) + report_error("cannot read profile", std::move(E)); if (!opts::SaveProfile.empty()) { YAMLProfileWriter PW(opts::SaveProfile); - PW.writeProfile(*this); + PW.writeProfile(*this, ProfileReaders); } if (opts::AggregateOnly && opts::ProfileFormat == opts::ProfileFormatKind::PF_YAML) { YAMLProfileWriter PW(opts::OutputFilename); - PW.writeProfile(*this); + PW.writeProfile(*this, ProfileReaders); } - // Release memory used by profile reader. - ProfileReader.reset(); + for (std::unique_ptr &PR : ProfileReaders) { + // Release memory used by profile reader. + PR.reset(); + } if (opts::AggregateOnly) exit(0); diff --git a/bolt/lib/Utils/CommandLineOpts.cpp b/bolt/lib/Utils/CommandLineOpts.cpp --- a/bolt/lib/Utils/CommandLineOpts.cpp +++ b/bolt/lib/Utils/CommandLineOpts.cpp @@ -135,7 +135,7 @@ cl::Optional, cl::cat(BoltOutputCategory)); -cl::opt PerfData("perfdata", cl::desc(""), cl::Optional, +cl::list PerfData("perfdata", cl::desc(""), cl::Optional, cl::cat(AggregatorCategory), cl::sub(cl::SubCommand::getAll())); diff --git a/bolt/tools/driver/llvm-bolt.cpp b/bolt/tools/driver/llvm-bolt.cpp --- a/bolt/tools/driver/llvm-bolt.cpp +++ b/bolt/tools/driver/llvm-bolt.cpp @@ -51,7 +51,7 @@ cl::Required, cl::cat(BoltCategory), cl::sub(cl::SubCommand::getAll())); -static cl::opt +static cl::list InputDataFilename("data", cl::desc(""), cl::Optional, @@ -63,7 +63,7 @@ cl::aliasopt(InputDataFilename), cl::cat(BoltCategory)); -static cl::opt +static cl::list InputDataFilename2("data2", cl::desc(""), cl::Optional, @@ -112,12 +112,14 @@ errs() << ToolName << ": unknown -data option.\n"; exit(1); } - if (!sys::fs::exists(opts::PerfData)) - report_error(opts::PerfData, errc::no_such_file_or_directory); - if (!DataAggregator::checkPerfDataMagic(opts::PerfData)) { - errs() << ToolName << ": '" << opts::PerfData - << "': expected valid perf.data file.\n"; - exit(1); + for (StringRef Filename : opts::PerfData) { + if (!sys::fs::exists(Filename)) + report_error(Filename, errc::no_such_file_or_directory); + if (!DataAggregator::checkPerfDataMagic(Filename)) { + errs() << ToolName << ": '" << Filename + << "': expected valid perf.data file.\n"; + exit(1); + } } if (opts::OutputFilename.empty()) { errs() << ToolName << ": expected -o= option.\n"; @@ -228,13 +230,13 @@ "-aggregate-only or perf2bolt.\n!!! Proceed on your own " "risk. !!!\n"; } - if (Error E = RI.setProfile(opts::PerfData)) - report_error(opts::PerfData, std::move(E)); - } - if (!opts::InputDataFilename.empty()) { - if (Error E = RI.setProfile(opts::InputDataFilename)) - report_error(opts::InputDataFilename, std::move(E)); + for (StringRef Filename : opts::PerfData) + if (Error E = RI.setProfile(Filename)) + report_error(Filename, std::move(E)); } + for (StringRef Filename : opts::InputDataFilename) + if (Error E = RI.setProfile(Filename)) + report_error(Filename, std::move(E)); if (opts::AggregateOnly && opts::PerfData.empty()) { errs() << ToolName << ": missing required -perfdata option.\n"; exit(1); @@ -248,9 +250,9 @@ report_error(opts::InputFilename, std::move(E)); MachORewriteInstance &MachORI = *MachORIOrErr.get(); - if (!opts::InputDataFilename.empty()) - if (Error E = MachORI.setProfile(opts::InputDataFilename)) - report_error(opts::InputDataFilename, std::move(E)); + for (StringRef Filename : opts::InputDataFilename) + if (Error E = MachORI.setProfile(Filename)) + report_error(Filename, std::move(E)); MachORI.run(); } else { @@ -277,24 +279,36 @@ if (Error E = RI1OrErr.takeError()) report_error(opts::InputFilename, std::move(E)); RewriteInstance &RI1 = *RI1OrErr.get(); - if (Error E = RI1.setProfile(opts::InputDataFilename)) - report_error(opts::InputDataFilename, std::move(E)); + for (StringRef Filename : opts::InputDataFilename) + if (Error E = RI1.setProfile(Filename)) + report_error(Filename, std::move(E)); auto RI2OrErr = RewriteInstance::create(ELFObj2, argc, argv, ToolPath); if (Error E = RI2OrErr.takeError()) report_error(opts::InputFilename2, std::move(E)); RewriteInstance &RI2 = *RI2OrErr.get(); - if (Error E = RI2.setProfile(opts::InputDataFilename2)) - report_error(opts::InputDataFilename2, std::move(E)); + for (StringRef Filename : opts::InputDataFilename2) + if (Error E = RI2.setProfile(Filename)) + report_error(Filename, std::move(E)); outs() << "BOLT-DIFF: *** Analyzing binary 1: " << opts::InputFilename << "\n"; - outs() << "BOLT-DIFF: *** Binary 1 fdata: " << opts::InputDataFilename - << "\n"; + outs() << "BOLT-DIFF: *** Binary 1 fdata: "; + { + ListSeparator LS; + for (StringRef Filename : opts::InputDataFilename) + outs() << LS << Filename; + outs() << "\n"; + } if (Error E = RI1.run()) report_error(opts::InputFilename, std::move(E)); outs() << "BOLT-DIFF: *** Analyzing binary 2: " << opts::InputFilename2 << "\n"; - outs() << "BOLT-DIFF: *** Binary 2 fdata: " - << opts::InputDataFilename2 << "\n"; + outs() << "BOLT-DIFF: *** Binary 2 fdata: "; + { + ListSeparator LS; + for (StringRef Filename : opts::InputDataFilename2) + outs() << LS << Filename; + outs() << "\n"; + } if (Error E = RI2.run()) report_error(opts::InputFilename2, std::move(E)); RI1.compare(RI2);