| //===-- 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 "CGDataOptions.h" | |
| #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/WithColor.h" | |
| #define DEBUG_TYPE "cg-data" | |
| using namespace llvm; | |
| using namespace cgdata; | |
| 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 | |
| bool llvm::cgdata::thinLTOTwoRounds( | |
| const cl::LibraryOptionsContext &LibraryOptions) { | |
| return LibraryOptions.get<CGDataOptions>().codegen_data_thinlto_two_rounds; | |
| } | |
| 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(const cl::LibraryOptionsContext &LibraryOptions) { | |
| std::call_once(CodeGenData::OnceFlag, [&LibraryOptions]() { | |
| Instance = std::unique_ptr<CodeGenData>(new CodeGenData()); | |
| const CGDataOptions &Opts = LibraryOptions.get<CGDataOptions>(); | |
| if (Opts.codegen_data_generate || Opts.codegen_data_thinlto_two_rounds) | |
| Instance->EmitCGData = true; | |
| else if (!Opts.codegen_data_use_path.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( | |
| LibraryOptions, Opts.codegen_data_use_path, *FS, | |
| Opts.indexed_codegen_data_lazy_loading); | |
| if (Error E = ReaderOrErr.takeError()) { | |
| warn(std::move(E), Opts.codegen_data_use_path); | |
| 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 == Version4, | |
| "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); | |
| if (Error Err = Stream->commit()) | |
| report_fatal_error(std::move(Err)); | |
| } | |
| 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(const cl::LibraryOptionsContext &LibraryOptions, | |
| 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(LibraryOptions); | |
| if (!GlobalOutlineRecord.empty()) | |
| cgdata::publishOutlinedHashTree(LibraryOptions, | |
| std::move(GlobalOutlineRecord.HashTree)); | |
| if (!GlobalStableFunctionMapRecord.empty()) | |
| cgdata::publishStableFunctionMap( | |
| LibraryOptions, std::move(GlobalStableFunctionMapRecord.FunctionMap)); | |
| return CombinedHash; | |
| } | |
| } // end namespace cgdata | |
| } // end namespace llvm |