Path: blob/main/contrib/llvm-project/llvm/lib/CGData/CodeGenData.cpp
213764 views
//===-- CodeGenData.cpp ---------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file contains support for codegen data that has stable summary which9// can be used to optimize the code in the subsequent codegen.10//11//===----------------------------------------------------------------------===//1213#include "llvm/Bitcode/BitcodeWriter.h"14#include "llvm/CGData/CodeGenDataReader.h"15#include "llvm/CGData/OutlinedHashTreeRecord.h"16#include "llvm/CGData/StableFunctionMapRecord.h"17#include "llvm/Object/ObjectFile.h"18#include "llvm/Support/Caching.h"19#include "llvm/Support/CommandLine.h"20#include "llvm/Support/WithColor.h"2122#define DEBUG_TYPE "cg-data"2324using namespace llvm;25using namespace cgdata;2627static cl::opt<bool>28CodeGenDataGenerate("codegen-data-generate", cl::init(false), cl::Hidden,29cl::desc("Emit CodeGen Data into custom sections"));30static cl::opt<std::string>31CodeGenDataUsePath("codegen-data-use-path", cl::init(""), cl::Hidden,32cl::desc("File path to where .cgdata file is read"));33cl::opt<bool> CodeGenDataThinLTOTwoRounds(34"codegen-data-thinlto-two-rounds", cl::init(false), cl::Hidden,35cl::desc("Enable two-round ThinLTO code generation. The first round "36"emits codegen data, while the second round uses the emitted "37"codegen data for further optimizations."));3839static std::string getCGDataErrString(cgdata_error Err,40const std::string &ErrMsg = "") {41std::string Msg;42raw_string_ostream OS(Msg);4344switch (Err) {45case cgdata_error::success:46OS << "success";47break;48case cgdata_error::eof:49OS << "end of File";50break;51case cgdata_error::bad_magic:52OS << "invalid codegen data (bad magic)";53break;54case cgdata_error::bad_header:55OS << "invalid codegen data (file header is corrupt)";56break;57case cgdata_error::empty_cgdata:58OS << "empty codegen data";59break;60case cgdata_error::malformed:61OS << "malformed codegen data";62break;63case cgdata_error::unsupported_version:64OS << "unsupported codegen data version";65break;66}6768// If optional error message is not empty, append it to the message.69if (!ErrMsg.empty())70OS << ": " << ErrMsg;7172return OS.str();73}7475namespace {7677// FIXME: This class is only here to support the transition to llvm::Error. It78// will be removed once this transition is complete. Clients should prefer to79// deal with the Error value directly, rather than converting to error_code.80class CGDataErrorCategoryType : public std::error_category {81const char *name() const noexcept override { return "llvm.cgdata"; }8283std::string message(int IE) const override {84return getCGDataErrString(static_cast<cgdata_error>(IE));85}86};8788} // end anonymous namespace8990const std::error_category &llvm::cgdata_category() {91static CGDataErrorCategoryType ErrorCategory;92return ErrorCategory;93}9495std::string CGDataError::message() const {96return getCGDataErrString(Err, Msg);97}9899char CGDataError::ID = 0;100101namespace {102103const char *CodeGenDataSectNameCommon[] = {104#define CG_DATA_SECT_ENTRY(Kind, SectNameCommon, SectNameCoff, Prefix) \105SectNameCommon,106#include "llvm/CGData/CodeGenData.inc"107};108109const char *CodeGenDataSectNameCoff[] = {110#define CG_DATA_SECT_ENTRY(Kind, SectNameCommon, SectNameCoff, Prefix) \111SectNameCoff,112#include "llvm/CGData/CodeGenData.inc"113};114115const char *CodeGenDataSectNamePrefix[] = {116#define CG_DATA_SECT_ENTRY(Kind, SectNameCommon, SectNameCoff, Prefix) Prefix,117#include "llvm/CGData/CodeGenData.inc"118};119120} // namespace121122namespace llvm {123124std::string getCodeGenDataSectionName(CGDataSectKind CGSK,125Triple::ObjectFormatType OF,126bool AddSegmentInfo) {127std::string SectName;128129if (OF == Triple::MachO && AddSegmentInfo)130SectName = CodeGenDataSectNamePrefix[CGSK];131132if (OF == Triple::COFF)133SectName += CodeGenDataSectNameCoff[CGSK];134else135SectName += CodeGenDataSectNameCommon[CGSK];136137return SectName;138}139140std::unique_ptr<CodeGenData> CodeGenData::Instance = nullptr;141std::once_flag CodeGenData::OnceFlag;142143CodeGenData &CodeGenData::getInstance() {144std::call_once(CodeGenData::OnceFlag, []() {145Instance = std::unique_ptr<CodeGenData>(new CodeGenData());146147if (CodeGenDataGenerate || CodeGenDataThinLTOTwoRounds)148Instance->EmitCGData = true;149else if (!CodeGenDataUsePath.empty()) {150// Initialize the global CGData if the input file name is given.151// We do not error-out when failing to parse the input file.152// Instead, just emit an warning message and fall back as if no CGData153// were available.154auto FS = vfs::getRealFileSystem();155auto ReaderOrErr = CodeGenDataReader::create(CodeGenDataUsePath, *FS);156if (Error E = ReaderOrErr.takeError()) {157warn(std::move(E), CodeGenDataUsePath);158return;159}160// Publish each CGData based on the data type in the header.161auto Reader = ReaderOrErr->get();162if (Reader->hasOutlinedHashTree())163Instance->publishOutlinedHashTree(Reader->releaseOutlinedHashTree());164if (Reader->hasStableFunctionMap())165Instance->publishStableFunctionMap(Reader->releaseStableFunctionMap());166}167});168return *Instance;169}170171namespace IndexedCGData {172173Expected<Header> Header::readFromBuffer(const unsigned char *Curr) {174using namespace support;175176static_assert(std::is_standard_layout_v<llvm::IndexedCGData::Header>,177"The header should be standard layout type since we use offset "178"of fields to read.");179Header H;180H.Magic = endian::readNext<uint64_t, endianness::little, unaligned>(Curr);181if (H.Magic != IndexedCGData::Magic)182return make_error<CGDataError>(cgdata_error::bad_magic);183H.Version = endian::readNext<uint32_t, endianness::little, unaligned>(Curr);184if (H.Version > IndexedCGData::CGDataVersion::CurrentVersion)185return make_error<CGDataError>(cgdata_error::unsupported_version);186H.DataKind = endian::readNext<uint32_t, endianness::little, unaligned>(Curr);187188static_assert(IndexedCGData::CGDataVersion::CurrentVersion == Version3,189"Please update the offset computation below if a new field has "190"been added to the header.");191H.OutlinedHashTreeOffset =192endian::readNext<uint64_t, endianness::little, unaligned>(Curr);193if (H.Version >= 2)194H.StableFunctionMapOffset =195endian::readNext<uint64_t, endianness::little, unaligned>(Curr);196197return H;198}199200} // end namespace IndexedCGData201202namespace cgdata {203204void warn(Twine Message, StringRef Whence, StringRef Hint) {205WithColor::warning();206if (!Whence.empty())207errs() << Whence << ": ";208errs() << Message << "\n";209if (!Hint.empty())210WithColor::note() << Hint << "\n";211}212213void warn(Error E, StringRef Whence) {214if (E.isA<CGDataError>()) {215handleAllErrors(std::move(E), [&](const CGDataError &IPE) {216warn(IPE.message(), Whence, "");217});218}219}220221void saveModuleForTwoRounds(const Module &TheModule, unsigned Task,222AddStreamFn AddStream) {223LLVM_DEBUG(dbgs() << "Saving module: " << TheModule.getModuleIdentifier()224<< " in Task " << Task << "\n");225Expected<std::unique_ptr<CachedFileStream>> StreamOrErr =226AddStream(Task, TheModule.getModuleIdentifier());227if (Error Err = StreamOrErr.takeError())228report_fatal_error(std::move(Err));229std::unique_ptr<CachedFileStream> &Stream = *StreamOrErr;230231WriteBitcodeToFile(TheModule, *Stream->OS,232/*ShouldPreserveUseListOrder=*/true);233234if (Error Err = Stream->commit())235report_fatal_error(std::move(Err));236}237238std::unique_ptr<Module> loadModuleForTwoRounds(BitcodeModule &OrigModule,239unsigned Task,240LLVMContext &Context,241ArrayRef<StringRef> IRFiles) {242LLVM_DEBUG(dbgs() << "Loading module: " << OrigModule.getModuleIdentifier()243<< " in Task " << Task << "\n");244auto FileBuffer = MemoryBuffer::getMemBuffer(245IRFiles[Task], "in-memory IR file", /*RequiresNullTerminator=*/false);246auto RestoredModule = parseBitcodeFile(*FileBuffer, Context);247if (!RestoredModule)248report_fatal_error(249Twine("Failed to parse optimized bitcode loaded for Task: ") +250Twine(Task) + "\n");251252// Restore the original module identifier.253(*RestoredModule)->setModuleIdentifier(OrigModule.getModuleIdentifier());254return std::move(*RestoredModule);255}256257Expected<stable_hash> mergeCodeGenData(ArrayRef<StringRef> ObjFiles) {258OutlinedHashTreeRecord GlobalOutlineRecord;259StableFunctionMapRecord GlobalStableFunctionMapRecord;260stable_hash CombinedHash = 0;261for (auto File : ObjFiles) {262if (File.empty())263continue;264std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(265File, "in-memory object file", /*RequiresNullTerminator=*/false);266Expected<std::unique_ptr<object::ObjectFile>> BinOrErr =267object::ObjectFile::createObjectFile(Buffer->getMemBufferRef());268if (!BinOrErr)269return BinOrErr.takeError();270271std::unique_ptr<object::ObjectFile> &Obj = BinOrErr.get();272if (auto E = CodeGenDataReader::mergeFromObjectFile(273Obj.get(), GlobalOutlineRecord, GlobalStableFunctionMapRecord,274&CombinedHash))275return E;276}277278GlobalStableFunctionMapRecord.finalize();279280if (!GlobalOutlineRecord.empty())281cgdata::publishOutlinedHashTree(std::move(GlobalOutlineRecord.HashTree));282if (!GlobalStableFunctionMapRecord.empty())283cgdata::publishStableFunctionMap(284std::move(GlobalStableFunctionMapRecord.FunctionMap));285286return CombinedHash;287}288289} // end namespace cgdata290291} // end namespace llvm292293294