forked from OSchip/llvm-project
279 lines
11 KiB
C++
279 lines
11 KiB
C++
//===--------- llvm-remarkutil/RemarkUtil.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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
/// Utility for remark files.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm-c/Remarks.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Remarks/Remark.h"
|
|
#include "llvm/Remarks/RemarkFormat.h"
|
|
#include "llvm/Remarks/RemarkParser.h"
|
|
#include "llvm/Remarks/YAMLRemarkSerializer.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/Compiler.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/InitLLVM.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/ToolOutputFile.h"
|
|
#include "llvm/Support/WithColor.h"
|
|
|
|
using namespace llvm;
|
|
using namespace remarks;
|
|
|
|
static ExitOnError ExitOnErr;
|
|
static cl::OptionCategory RemarkUtilCategory("llvm-remarkutil options");
|
|
namespace subopts {
|
|
static cl::SubCommand
|
|
YAML2Bitstream("yaml2bitstream",
|
|
"Convert YAML remarks to bitstream remarks");
|
|
static cl::SubCommand
|
|
Bitstream2YAML("bitstream2yaml",
|
|
"Convert bitstream remarks to YAML remarks");
|
|
static cl::SubCommand InstructionCount(
|
|
"instruction-count",
|
|
"Function instruction count information (requires asm-printer remarks)");
|
|
} // namespace subopts
|
|
|
|
// Keep input + output help + names consistent across the various modes via a
|
|
// hideous macro.
|
|
#define INPUT_OUTPUT_COMMAND_LINE_OPTIONS(SUBOPT) \
|
|
static cl::opt<std::string> InputFileName( \
|
|
cl::Positional, cl::cat(RemarkUtilCategory), cl::init("-"), \
|
|
cl::desc("<input file>"), cl::sub(SUBOPT)); \
|
|
static cl::opt<std::string> OutputFileName( \
|
|
"o", cl::init("-"), cl::cat(RemarkUtilCategory), cl::desc("Output"), \
|
|
cl::value_desc("filename"), cl::sub(SUBOPT));
|
|
namespace yaml2bitstream {
|
|
/// Remark format to parse.
|
|
static constexpr Format InputFormat = Format::YAML;
|
|
/// Remark format to output.
|
|
static constexpr Format OutputFormat = Format::Bitstream;
|
|
INPUT_OUTPUT_COMMAND_LINE_OPTIONS(subopts::YAML2Bitstream)
|
|
} // namespace yaml2bitstream
|
|
|
|
namespace bitstream2yaml {
|
|
/// Remark format to parse.
|
|
static constexpr Format InputFormat = Format::Bitstream;
|
|
/// Remark format to output.
|
|
static constexpr Format OutputFormat = Format::YAML;
|
|
INPUT_OUTPUT_COMMAND_LINE_OPTIONS(subopts::Bitstream2YAML)
|
|
} // namespace bitstream2yaml
|
|
|
|
namespace instructioncount {
|
|
static cl::opt<Format> InputFormat(
|
|
"parser", cl::desc("Input remark format to parse"),
|
|
cl::values(clEnumValN(Format::YAML, "yaml", "YAML"),
|
|
clEnumValN(Format::Bitstream, "bitstream", "Bitstream")),
|
|
cl::sub(subopts::InstructionCount));
|
|
INPUT_OUTPUT_COMMAND_LINE_OPTIONS(subopts::InstructionCount)
|
|
} // namespace instructioncount
|
|
|
|
/// \returns A MemoryBuffer for the input file on success, and an Error
|
|
/// otherwise.
|
|
static Expected<std::unique_ptr<MemoryBuffer>>
|
|
getInputMemoryBuffer(StringRef InputFileName) {
|
|
auto MaybeBuf = MemoryBuffer::getFileOrSTDIN(InputFileName);
|
|
if (auto ErrorCode = MaybeBuf.getError())
|
|
return createStringError(ErrorCode,
|
|
Twine("Cannot open file '" + InputFileName +
|
|
"': " + ErrorCode.message()));
|
|
return std::move(*MaybeBuf);
|
|
}
|
|
|
|
/// \returns A ToolOutputFile which can be used for outputting the results of
|
|
/// some tool mode.
|
|
/// \p OutputFileName is the desired destination.
|
|
/// \p Flags controls whether or not the file is opened for writing in text
|
|
/// mode, as a binary, etc. See sys::fs::OpenFlags for more detail.
|
|
static Expected<std::unique_ptr<ToolOutputFile>>
|
|
getOutputFileWithFlags(StringRef OutputFileName, sys::fs::OpenFlags Flags) {
|
|
if (OutputFileName == "")
|
|
OutputFileName = "-";
|
|
std::error_code ErrorCode;
|
|
auto OF = std::make_unique<ToolOutputFile>(OutputFileName, ErrorCode, Flags);
|
|
if (ErrorCode)
|
|
return errorCodeToError(ErrorCode);
|
|
return std::move(OF);
|
|
}
|
|
|
|
/// \returns A ToolOutputFile which can be used for writing remarks on success,
|
|
/// and an Error otherwise.
|
|
/// \p OutputFileName is the desired destination.
|
|
/// \p OutputFormat
|
|
static Expected<std::unique_ptr<ToolOutputFile>>
|
|
getOutputFileForRemarks(StringRef OutputFileName, Format OutputFormat) {
|
|
assert((OutputFormat == Format::YAML || OutputFormat == Format::Bitstream) &&
|
|
"Expected one of YAML or Bitstream!");
|
|
return getOutputFileWithFlags(OutputFileName, OutputFormat == Format::YAML
|
|
? sys::fs::OF_TextWithCRLF
|
|
: sys::fs::OF_None);
|
|
}
|
|
|
|
namespace yaml2bitstream {
|
|
/// Parses all remarks in the input YAML file.
|
|
/// \p [out] ParsedRemarks - Filled with remarks parsed from the input file.
|
|
/// \p [out] StrTab - A string table populated for later remark serialization.
|
|
/// \returns Error::success() if all remarks were successfully parsed, and an
|
|
/// Error otherwise.
|
|
static Error
|
|
tryParseRemarksFromYAMLFile(std::vector<std::unique_ptr<Remark>> &ParsedRemarks,
|
|
StringTable &StrTab) {
|
|
auto MaybeBuf = getInputMemoryBuffer(InputFileName);
|
|
if (!MaybeBuf)
|
|
return MaybeBuf.takeError();
|
|
auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer());
|
|
if (!MaybeParser)
|
|
return MaybeParser.takeError();
|
|
auto &Parser = **MaybeParser;
|
|
auto MaybeRemark = Parser.next();
|
|
for (; MaybeRemark; MaybeRemark = Parser.next()) {
|
|
StrTab.internalize(**MaybeRemark);
|
|
ParsedRemarks.push_back(std::move(*MaybeRemark));
|
|
}
|
|
auto E = MaybeRemark.takeError();
|
|
if (!E.isA<EndOfFileError>())
|
|
return E;
|
|
consumeError(std::move(E));
|
|
return Error::success();
|
|
}
|
|
|
|
/// Reserialize a list of parsed YAML remarks into bitstream remarks.
|
|
/// \p ParsedRemarks - A list of remarks.
|
|
/// \p StrTab - The string table for the remarks.
|
|
/// \returns Error::success() on success.
|
|
static Error tryReserializeYAML2Bitstream(
|
|
const std::vector<std::unique_ptr<Remark>> &ParsedRemarks,
|
|
StringTable &StrTab) {
|
|
auto MaybeOF = getOutputFileForRemarks(OutputFileName, OutputFormat);
|
|
if (!MaybeOF)
|
|
return MaybeOF.takeError();
|
|
auto OF = std::move(*MaybeOF);
|
|
auto MaybeSerializer = createRemarkSerializer(
|
|
OutputFormat, SerializerMode::Standalone, OF->os(), std::move(StrTab));
|
|
if (!MaybeSerializer)
|
|
return MaybeSerializer.takeError();
|
|
auto Serializer = std::move(*MaybeSerializer);
|
|
for (const auto &Remark : ParsedRemarks)
|
|
Serializer->emit(*Remark);
|
|
OF->keep();
|
|
return Error::success();
|
|
}
|
|
|
|
/// Parse YAML remarks and reserialize as bitstream remarks.
|
|
/// \returns Error::success() on success, and an Error otherwise.
|
|
static Error tryYAML2Bitstream() {
|
|
StringTable StrTab;
|
|
std::vector<std::unique_ptr<Remark>> ParsedRemarks;
|
|
ExitOnErr(tryParseRemarksFromYAMLFile(ParsedRemarks, StrTab));
|
|
return tryReserializeYAML2Bitstream(ParsedRemarks, StrTab);
|
|
}
|
|
} // namespace yaml2bitstream
|
|
|
|
namespace bitstream2yaml {
|
|
/// Parse bitstream remarks and reserialize as YAML remarks.
|
|
/// \returns An Error if reserialization fails, or Error::success() on success.
|
|
static Error tryBitstream2YAML() {
|
|
// Create the serializer.
|
|
auto MaybeOF = getOutputFileForRemarks(OutputFileName, OutputFormat);
|
|
if (!MaybeOF)
|
|
return MaybeOF.takeError();
|
|
auto OF = std::move(*MaybeOF);
|
|
auto MaybeSerializer = createRemarkSerializer(
|
|
OutputFormat, SerializerMode::Standalone, OF->os());
|
|
if (!MaybeSerializer)
|
|
return MaybeSerializer.takeError();
|
|
|
|
// Create the parser.
|
|
auto MaybeBuf = getInputMemoryBuffer(InputFileName);
|
|
if (!MaybeBuf)
|
|
return MaybeBuf.takeError();
|
|
auto Serializer = std::move(*MaybeSerializer);
|
|
auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer());
|
|
if (!MaybeParser)
|
|
return MaybeParser.takeError();
|
|
auto &Parser = **MaybeParser;
|
|
|
|
// Parse + reserialize all remarks.
|
|
auto MaybeRemark = Parser.next();
|
|
for (; MaybeRemark; MaybeRemark = Parser.next())
|
|
Serializer->emit(**MaybeRemark);
|
|
auto E = MaybeRemark.takeError();
|
|
if (!E.isA<EndOfFileError>())
|
|
return E;
|
|
consumeError(std::move(E));
|
|
return Error::success();
|
|
}
|
|
} // namespace bitstream2yaml
|
|
|
|
namespace instructioncount {
|
|
/// Outputs all instruction count remarks in the file as a CSV.
|
|
/// \returns Error::success() on success, and an Error otherwise.
|
|
static Error tryInstructionCount() {
|
|
// Create the output buffer.
|
|
auto MaybeOF = getOutputFileWithFlags(OutputFileName,
|
|
/*Flags = */ sys::fs::OF_TextWithCRLF);
|
|
if (!MaybeOF)
|
|
return MaybeOF.takeError();
|
|
auto OF = std::move(*MaybeOF);
|
|
// Create a parser for the user-specified input format.
|
|
auto MaybeBuf = getInputMemoryBuffer(InputFileName);
|
|
if (!MaybeBuf)
|
|
return MaybeBuf.takeError();
|
|
auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer());
|
|
if (!MaybeParser)
|
|
return MaybeParser.takeError();
|
|
// Emit CSV header.
|
|
OF->os() << "Function,InstructionCount\n";
|
|
// Parse all remarks. Whenever we see an instruction count remark, output
|
|
// the file name and the number of instructions.
|
|
auto &Parser = **MaybeParser;
|
|
auto MaybeRemark = Parser.next();
|
|
for (; MaybeRemark; MaybeRemark = Parser.next()) {
|
|
auto &Remark = **MaybeRemark;
|
|
if (Remark.RemarkName != "InstructionCount")
|
|
continue;
|
|
auto *InstrCountArg = find_if(Remark.Args, [](const Argument &Arg) {
|
|
return Arg.Key == "NumInstructions";
|
|
});
|
|
assert(InstrCountArg != Remark.Args.end() &&
|
|
"Expected instruction count remarks to have a NumInstructions key?");
|
|
OF->os() << Remark.FunctionName << "," << InstrCountArg->Val << "\n";
|
|
}
|
|
auto E = MaybeRemark.takeError();
|
|
if (!E.isA<EndOfFileError>())
|
|
return E;
|
|
consumeError(std::move(E));
|
|
OF->keep();
|
|
return Error::success();
|
|
}
|
|
} // namespace instructioncount
|
|
|
|
/// Handle user-specified suboptions (e.g. yaml2bitstream, bitstream2yaml).
|
|
/// \returns An Error if the specified suboption fails or if no suboption was
|
|
/// specified. Otherwise, Error::success().
|
|
static Error handleSuboptions() {
|
|
if (subopts::Bitstream2YAML)
|
|
return bitstream2yaml::tryBitstream2YAML();
|
|
if (subopts::YAML2Bitstream)
|
|
return yaml2bitstream::tryYAML2Bitstream();
|
|
if (subopts::InstructionCount)
|
|
return instructioncount::tryInstructionCount();
|
|
return make_error<StringError>(
|
|
"Please specify a subcommand. (See -help for options)",
|
|
inconvertibleErrorCode());
|
|
}
|
|
|
|
int main(int argc, const char **argv) {
|
|
InitLLVM X(argc, argv);
|
|
cl::HideUnrelatedOptions(RemarkUtilCategory);
|
|
cl::ParseCommandLineOptions(argc, argv, "Remark file utilities\n");
|
|
ExitOnErr.setBanner(std::string(argv[0]) + ": error: ");
|
|
ExitOnErr(handleSuboptions());
|
|
}
|