
The new tool constructs a histogram of instruction frequencies, optionally filtered by function name via a regex. It can display in either a human-readable table format, or machine-readable CSV.
144 lines
4.8 KiB
C++
144 lines
4.8 KiB
C++
//===- RemarkInstructionMix.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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Generic tool to extract instruction mix from asm-printer remarks.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "RemarkUtilHelpers.h"
|
|
#include "RemarkUtilRegistry.h"
|
|
|
|
#include "llvm/Support/Format.h"
|
|
#include "llvm/Support/FormattedStream.h"
|
|
#include "llvm/Support/Regex.h"
|
|
|
|
#include <cmath>
|
|
#include <numeric>
|
|
|
|
using namespace llvm;
|
|
using namespace remarks;
|
|
using namespace llvm::remarkutil;
|
|
|
|
namespace instructionmix {
|
|
|
|
static cl::SubCommand
|
|
InstructionMix("instruction-mix",
|
|
"Instruction Mix (requires asm-printer remarks)");
|
|
|
|
static cl::opt<std::string>
|
|
FunctionFilter("filter", cl::sub(InstructionMix), cl::ValueOptional,
|
|
cl::desc("Optional function name to filter collection by"));
|
|
|
|
static cl::opt<std::string>
|
|
FunctionFilterRE("rfilter", cl::sub(InstructionMix), cl::ValueOptional,
|
|
cl::desc("Optional function name to filter collection by "
|
|
"(accepts regular expressions)"));
|
|
|
|
enum ReportStyleOptions { human_output, csv_output };
|
|
static cl::opt<ReportStyleOptions> ReportStyle(
|
|
"report_style", cl::sub(InstructionMix),
|
|
cl::init(ReportStyleOptions::human_output),
|
|
cl::desc("Choose the report output format:"),
|
|
cl::values(clEnumValN(human_output, "human", "Human-readable format"),
|
|
clEnumValN(csv_output, "csv", "CSV format")));
|
|
|
|
INPUT_FORMAT_COMMAND_LINE_OPTIONS(InstructionMix)
|
|
INPUT_OUTPUT_COMMAND_LINE_OPTIONS(InstructionMix)
|
|
|
|
static Error tryInstructionMix() {
|
|
auto MaybeOF =
|
|
getOutputFileWithFlags(OutputFileName, sys::fs::OF_TextWithCRLF);
|
|
if (!MaybeOF)
|
|
return MaybeOF.takeError();
|
|
|
|
auto OF = std::move(*MaybeOF);
|
|
auto MaybeBuf = getInputMemoryBuffer(InputFileName);
|
|
if (!MaybeBuf)
|
|
return MaybeBuf.takeError();
|
|
auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer());
|
|
if (!MaybeParser)
|
|
return MaybeParser.takeError();
|
|
|
|
Expected<std::optional<FilterMatcher>> Filter =
|
|
FilterMatcher::createExactOrRE(FunctionFilter, FunctionFilterRE);
|
|
if (!Filter)
|
|
return Filter.takeError();
|
|
|
|
// Collect the histogram of instruction counts.
|
|
llvm::DenseMap<StringRef, unsigned> Histogram;
|
|
auto &Parser = **MaybeParser;
|
|
auto MaybeRemark = Parser.next();
|
|
for (; MaybeRemark; MaybeRemark = Parser.next()) {
|
|
Remark &Remark = **MaybeRemark;
|
|
if (Remark.RemarkName != "InstructionMix")
|
|
continue;
|
|
if (*Filter && !(*Filter)->match(Remark.FunctionName))
|
|
continue;
|
|
for (auto &Arg : Remark.Args) {
|
|
StringRef Key = Arg.Key;
|
|
if (!Key.consume_front("INST_"))
|
|
continue;
|
|
unsigned Val = 0;
|
|
bool ParseError = Arg.Val.getAsInteger(10, Val);
|
|
assert(!ParseError);
|
|
(void)ParseError;
|
|
Histogram[Key] += Val;
|
|
}
|
|
}
|
|
|
|
// Sort it.
|
|
using MixEntry = std::pair<StringRef, unsigned>;
|
|
llvm::SmallVector<MixEntry> Mix(Histogram.begin(), Histogram.end());
|
|
std::sort(Mix.begin(), Mix.end(), [](const auto &LHS, const auto &RHS) {
|
|
return LHS.second > RHS.second;
|
|
});
|
|
|
|
// Print the results.
|
|
switch (ReportStyle) {
|
|
case human_output: {
|
|
formatted_raw_ostream FOS(OF->os());
|
|
size_t MaxMnemonic =
|
|
std::accumulate(Mix.begin(), Mix.end(), StringRef("Instruction").size(),
|
|
[](size_t MaxMnemonic, const MixEntry &Elt) {
|
|
return std::max(MaxMnemonic, Elt.first.size());
|
|
});
|
|
unsigned MaxValue = std::accumulate(
|
|
Mix.begin(), Mix.end(), 1, [](unsigned MaxValue, const MixEntry &Elt) {
|
|
return std::max(MaxValue, Elt.second);
|
|
});
|
|
unsigned ValueWidth = std::log10(MaxValue) + 1;
|
|
FOS << "Instruction";
|
|
FOS.PadToColumn(MaxMnemonic + 1) << "Count\n";
|
|
FOS << "-----------";
|
|
FOS.PadToColumn(MaxMnemonic + 1) << "-----\n";
|
|
for (const auto &[Inst, Count] : Mix) {
|
|
FOS << Inst;
|
|
FOS.PadToColumn(MaxMnemonic + 1)
|
|
<< " " << format_decimal(Count, ValueWidth) << "\n";
|
|
}
|
|
} break;
|
|
case csv_output: {
|
|
OF->os() << "Instruction,Count\n";
|
|
for (const auto &[Inst, Count] : Mix)
|
|
OF->os() << Inst << "," << Count << "\n";
|
|
} break;
|
|
}
|
|
|
|
auto E = MaybeRemark.takeError();
|
|
if (!E.isA<EndOfFileError>())
|
|
return E;
|
|
consumeError(std::move(E));
|
|
OF->keep();
|
|
return Error::success();
|
|
}
|
|
|
|
static CommandRegistration InstructionMixReg(&InstructionMix,
|
|
tryInstructionMix);
|
|
|
|
} // namespace instructionmix
|