Files
clang-p2996/llvm/lib/CGData/CodeGenData.cpp
anjenner c3f815ba82 Modify the localCache API to require an explicit commit on CachedFile… (#136121)
…Stream.

CachedFileStream has previously performed the commit step in its
destructor, but this means its only recourse for error handling is
report_fatal_error. Modify this to add an explicit commit() method, and
call this in the appropriate places with appropriate error handling for
the location.

Currently the destructor of CacheStream gives an assert failure in Debug
builds if commit() was not called. This will help track down any
remaining uses of the API that assume the old destructior behaviour. In
Release builds we fall back to the previous behaviour and call
report_fatal_error if the commit fails.

This is version 2 of this PR, superseding reverted PR
https://github.com/llvm/llvm-project/pull/115331 . I have incorporated a
change to the testcase to make it more reliable on Windows, as well as
two follow-up changes
(df79000896
and
b0baa1d8bd)
that were also reverted when 115331 was reverted.

---------

Co-authored-by: Augie Fackler <augie@google.com>
Co-authored-by: Vitaly Buka <vitalybuka@google.com>
2025-04-22 09:45:15 +01:00

295 lines
10 KiB
C++

//===-- 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);
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(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