| //===-- CodeGenData.cpp ---------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file contains support for codegen data that has stable summary which |
| // can be used to optimize the code in the subsequent codegen. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/Bitcode/BitcodeWriter.h" |
| #include "llvm/CGData/CodeGenDataReader.h" |
| #include "llvm/CGData/OutlinedHashTreeRecord.h" |
| #include "llvm/CGData/StableFunctionMapRecord.h" |
| #include "llvm/Object/ObjectFile.h" |
| #include "llvm/Support/Caching.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/FileSystem.h" |
| #include "llvm/Support/Path.h" |
| #include "llvm/Support/WithColor.h" |
| |
| #define DEBUG_TYPE "cg-data" |
| |
| using namespace llvm; |
| using namespace cgdata; |
| |
| static cl::opt<bool> |
| CodeGenDataGenerate("codegen-data-generate", cl::init(false), cl::Hidden, |
| cl::desc("Emit CodeGen Data into custom sections")); |
| static cl::opt<std::string> |
| CodeGenDataUsePath("codegen-data-use-path", cl::init(""), cl::Hidden, |
| cl::desc("File path to where .cgdata file is read")); |
| cl::opt<bool> CodeGenDataThinLTOTwoRounds( |
| "codegen-data-thinlto-two-rounds", cl::init(false), cl::Hidden, |
| cl::desc("Enable two-round ThinLTO code generation. The first round " |
| "emits codegen data, while the second round uses the emitted " |
| "codegen data for further optimizations.")); |
| |
| static std::string getCGDataErrString(cgdata_error Err, |
| const std::string &ErrMsg = "") { |
| std::string Msg; |
| raw_string_ostream OS(Msg); |
| |
| switch (Err) { |
| case cgdata_error::success: |
| OS << "success"; |
| break; |
| case cgdata_error::eof: |
| OS << "end of File"; |
| break; |
| case cgdata_error::bad_magic: |
| OS << "invalid codegen data (bad magic)"; |
| break; |
| case cgdata_error::bad_header: |
| OS << "invalid codegen data (file header is corrupt)"; |
| break; |
| case cgdata_error::empty_cgdata: |
| OS << "empty codegen data"; |
| break; |
| case cgdata_error::malformed: |
| OS << "malformed codegen data"; |
| break; |
| case cgdata_error::unsupported_version: |
| OS << "unsupported codegen data version"; |
| break; |
| } |
| |
| // If optional error message is not empty, append it to the message. |
| if (!ErrMsg.empty()) |
| OS << ": " << ErrMsg; |
| |
| return OS.str(); |
| } |
| |
| namespace { |
| |
| // FIXME: This class is only here to support the transition to llvm::Error. It |
| // will be removed once this transition is complete. Clients should prefer to |
| // deal with the Error value directly, rather than converting to error_code. |
| class CGDataErrorCategoryType : public std::error_category { |
| const char *name() const noexcept override { return "llvm.cgdata"; } |
| |
| std::string message(int IE) const override { |
| return getCGDataErrString(static_cast<cgdata_error>(IE)); |
| } |
| }; |
| |
| } // end anonymous namespace |
| |
| const std::error_category &llvm::cgdata_category() { |
| static CGDataErrorCategoryType ErrorCategory; |
| return ErrorCategory; |
| } |
| |
| std::string CGDataError::message() const { |
| return getCGDataErrString(Err, Msg); |
| } |
| |
| char CGDataError::ID = 0; |
| |
| namespace { |
| |
| const char *CodeGenDataSectNameCommon[] = { |
| #define CG_DATA_SECT_ENTRY(Kind, SectNameCommon, SectNameCoff, Prefix) \ |
| SectNameCommon, |
| #include "llvm/CGData/CodeGenData.inc" |
| }; |
| |
| const char *CodeGenDataSectNameCoff[] = { |
| #define CG_DATA_SECT_ENTRY(Kind, SectNameCommon, SectNameCoff, Prefix) \ |
| SectNameCoff, |
| #include "llvm/CGData/CodeGenData.inc" |
| }; |
| |
| const char *CodeGenDataSectNamePrefix[] = { |
| #define CG_DATA_SECT_ENTRY(Kind, SectNameCommon, SectNameCoff, Prefix) Prefix, |
| #include "llvm/CGData/CodeGenData.inc" |
| }; |
| |
| } // namespace |
| |
| namespace llvm { |
| |
| std::string getCodeGenDataSectionName(CGDataSectKind CGSK, |
| Triple::ObjectFormatType OF, |
| bool AddSegmentInfo) { |
| std::string SectName; |
| |
| if (OF == Triple::MachO && AddSegmentInfo) |
| SectName = CodeGenDataSectNamePrefix[CGSK]; |
| |
| if (OF == Triple::COFF) |
| SectName += CodeGenDataSectNameCoff[CGSK]; |
| else |
| SectName += CodeGenDataSectNameCommon[CGSK]; |
| |
| return SectName; |
| } |
| |
| std::unique_ptr<CodeGenData> CodeGenData::Instance = nullptr; |
| std::once_flag CodeGenData::OnceFlag; |
| |
| CodeGenData &CodeGenData::getInstance() { |
| std::call_once(CodeGenData::OnceFlag, []() { |
| Instance = std::unique_ptr<CodeGenData>(new CodeGenData()); |
| |
| if (CodeGenDataGenerate || CodeGenDataThinLTOTwoRounds) |
| Instance->EmitCGData = true; |
| else if (!CodeGenDataUsePath.empty()) { |
| // Initialize the global CGData if the input file name is given. |
| // We do not error-out when failing to parse the input file. |
| // Instead, just emit an warning message and fall back as if no CGData |
| // were available. |
| auto FS = vfs::getRealFileSystem(); |
| auto ReaderOrErr = CodeGenDataReader::create(CodeGenDataUsePath, *FS); |
| if (Error E = ReaderOrErr.takeError()) { |
| warn(std::move(E), CodeGenDataUsePath); |
| return; |
| } |
| // Publish each CGData based on the data type in the header. |
| auto Reader = ReaderOrErr->get(); |
| if (Reader->hasOutlinedHashTree()) |
| Instance->publishOutlinedHashTree(Reader->releaseOutlinedHashTree()); |
| if (Reader->hasStableFunctionMap()) |
| Instance->publishStableFunctionMap(Reader->releaseStableFunctionMap()); |
| } |
| }); |
| return *Instance; |
| } |
| |
| namespace IndexedCGData { |
| |
| Expected<Header> Header::readFromBuffer(const unsigned char *Curr) { |
| using namespace support; |
| |
| static_assert(std::is_standard_layout_v<llvm::IndexedCGData::Header>, |
| "The header should be standard layout type since we use offset " |
| "of fields to read."); |
| Header H; |
| H.Magic = endian::readNext<uint64_t, endianness::little, unaligned>(Curr); |
| if (H.Magic != IndexedCGData::Magic) |
| return make_error<CGDataError>(cgdata_error::bad_magic); |
| H.Version = endian::readNext<uint32_t, endianness::little, unaligned>(Curr); |
| if (H.Version > IndexedCGData::CGDataVersion::CurrentVersion) |
| return make_error<CGDataError>(cgdata_error::unsupported_version); |
| H.DataKind = endian::readNext<uint32_t, endianness::little, unaligned>(Curr); |
| |
| static_assert(IndexedCGData::CGDataVersion::CurrentVersion == Version2, |
| "Please update the offset computation below if a new field has " |
| "been added to the header."); |
| H.OutlinedHashTreeOffset = |
| endian::readNext<uint64_t, endianness::little, unaligned>(Curr); |
| if (H.Version >= 2) |
| H.StableFunctionMapOffset = |
| endian::readNext<uint64_t, endianness::little, unaligned>(Curr); |
| |
| return H; |
| } |
| |
| } // end namespace IndexedCGData |
| |
| namespace cgdata { |
| |
| void warn(Twine Message, StringRef Whence, StringRef Hint) { |
| WithColor::warning(); |
| if (!Whence.empty()) |
| errs() << Whence << ": "; |
| errs() << Message << "\n"; |
| if (!Hint.empty()) |
| WithColor::note() << Hint << "\n"; |
| } |
| |
| void warn(Error E, StringRef Whence) { |
| if (E.isA<CGDataError>()) { |
| handleAllErrors(std::move(E), [&](const CGDataError &IPE) { |
| warn(IPE.message(), Whence, ""); |
| }); |
| } |
| } |
| |
| void saveModuleForTwoRounds(const Module &TheModule, unsigned Task, |
| AddStreamFn AddStream) { |
| LLVM_DEBUG(dbgs() << "Saving module: " << TheModule.getModuleIdentifier() |
| << " in Task " << Task << "\n"); |
| Expected<std::unique_ptr<CachedFileStream>> StreamOrErr = |
| AddStream(Task, TheModule.getModuleIdentifier()); |
| if (Error Err = StreamOrErr.takeError()) |
| report_fatal_error(std::move(Err)); |
| std::unique_ptr<CachedFileStream> &Stream = *StreamOrErr; |
| |
| WriteBitcodeToFile(TheModule, *Stream->OS, |
| /*ShouldPreserveUseListOrder=*/true); |
| } |
| |
| std::unique_ptr<Module> loadModuleForTwoRounds(BitcodeModule &OrigModule, |
| unsigned Task, |
| LLVMContext &Context, |
| ArrayRef<StringRef> IRFiles) { |
| LLVM_DEBUG(dbgs() << "Loading module: " << OrigModule.getModuleIdentifier() |
| << " in Task " << Task << "\n"); |
| auto FileBuffer = MemoryBuffer::getMemBuffer( |
| IRFiles[Task], "in-memory IR file", /*RequiresNullTerminator=*/false); |
| auto RestoredModule = parseBitcodeFile(*FileBuffer, Context); |
| if (!RestoredModule) |
| report_fatal_error( |
| Twine("Failed to parse optimized bitcode loaded for Task: ") + |
| Twine(Task) + "\n"); |
| |
| // Restore the original module identifier. |
| (*RestoredModule)->setModuleIdentifier(OrigModule.getModuleIdentifier()); |
| return std::move(*RestoredModule); |
| } |
| |
| Expected<stable_hash> mergeCodeGenData(ArrayRef<StringRef> ObjFiles) { |
| OutlinedHashTreeRecord GlobalOutlineRecord; |
| StableFunctionMapRecord GlobalStableFunctionMapRecord; |
| stable_hash CombinedHash = 0; |
| for (auto File : ObjFiles) { |
| if (File.empty()) |
| continue; |
| std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer( |
| File, "in-memory object file", /*RequiresNullTerminator=*/false); |
| Expected<std::unique_ptr<object::ObjectFile>> BinOrErr = |
| object::ObjectFile::createObjectFile(Buffer->getMemBufferRef()); |
| if (!BinOrErr) |
| return BinOrErr.takeError(); |
| |
| std::unique_ptr<object::ObjectFile> &Obj = BinOrErr.get(); |
| if (auto E = CodeGenDataReader::mergeFromObjectFile( |
| Obj.get(), GlobalOutlineRecord, GlobalStableFunctionMapRecord, |
| &CombinedHash)) |
| return E; |
| } |
| |
| GlobalStableFunctionMapRecord.finalize(); |
| |
| if (!GlobalOutlineRecord.empty()) |
| cgdata::publishOutlinedHashTree(std::move(GlobalOutlineRecord.HashTree)); |
| if (!GlobalStableFunctionMapRecord.empty()) |
| cgdata::publishStableFunctionMap( |
| std::move(GlobalStableFunctionMapRecord.FunctionMap)); |
| |
| return CombinedHash; |
| } |
| |
| } // end namespace cgdata |
| |
| } // end namespace llvm |