#include "Error.h"
#include "MCInstrDescView.h"
#include "PerfHelper.h"
+#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
Expected<InstructionBenchmark> BenchmarkRunner::runConfiguration(
const BenchmarkCode &BC, unsigned NumRepetitions,
- const SnippetRepetitor &Repetitor, bool DumpObjectToDisk) const {
+ ArrayRef<std::unique_ptr<const SnippetRepetitor>> Repetitors,
+ bool DumpObjectToDisk) const {
InstructionBenchmark InstrBenchmark;
InstrBenchmark.Mode = Mode;
InstrBenchmark.CpuName = std::string(State.getTargetMachine().getTargetCPU());
InstrBenchmark.Key = BC.Key;
- // Assemble at least kMinInstructionsForSnippet instructions by repeating the
- // snippet for debug/analysis. This is so that the user clearly understands
- // that the inside instructions are repeated.
- constexpr const int kMinInstructionsForSnippet = 16;
- {
- SmallString<0> Buffer;
- raw_svector_ostream OS(Buffer);
- if (Error E = assembleToStream(
- State.getExegesisTarget(), State.createTargetMachine(), BC.LiveIns,
- BC.Key.RegisterInitialValues,
- Repetitor.Repeat(Instructions, kMinInstructionsForSnippet), OS)) {
- return std::move(E);
+ // If we end up having an error, and we've previously succeeded with
+ // some other Repetitor, we want to discard the previous measurements.
+ struct ClearBenchmarkOnReturn {
+ ClearBenchmarkOnReturn(InstructionBenchmark *IB) : IB(IB) {}
+ ~ClearBenchmarkOnReturn() {
+ if (Clear)
+ IB->Measurements.clear();
+ }
+ void disarm() { Clear = false; }
+
+ private:
+ InstructionBenchmark *const IB;
+ bool Clear = true;
+ };
+ ClearBenchmarkOnReturn CBOR(&InstrBenchmark);
+
+ for (const std::unique_ptr<const SnippetRepetitor> &Repetitor : Repetitors) {
+ // Assemble at least kMinInstructionsForSnippet instructions by repeating
+ // the snippet for debug/analysis. This is so that the user clearly
+ // understands that the inside instructions are repeated.
+ constexpr const int kMinInstructionsForSnippet = 16;
+ {
+ SmallString<0> Buffer;
+ raw_svector_ostream OS(Buffer);
+ if (Error E = assembleToStream(
+ State.getExegesisTarget(), State.createTargetMachine(),
+ BC.LiveIns, BC.Key.RegisterInitialValues,
+ Repetitor->Repeat(Instructions, kMinInstructionsForSnippet),
+ OS)) {
+ return std::move(E);
+ }
+ const ExecutableFunction EF(State.createTargetMachine(),
+ getObjectFromBuffer(OS.str()));
+ const auto FnBytes = EF.getFunctionBytes();
+ InstrBenchmark.AssembledSnippet.insert(
+ InstrBenchmark.AssembledSnippet.end(), FnBytes.begin(),
+ FnBytes.end());
}
- const ExecutableFunction EF(State.createTargetMachine(),
- getObjectFromBuffer(OS.str()));
- const auto FnBytes = EF.getFunctionBytes();
- InstrBenchmark.AssembledSnippet.assign(FnBytes.begin(), FnBytes.end());
- }
- // Assemble NumRepetitions instructions repetitions of the snippet for
- // measurements.
- const auto Filler =
- Repetitor.Repeat(Instructions, InstrBenchmark.NumRepetitions);
+ // Assemble NumRepetitions instructions repetitions of the snippet for
+ // measurements.
+ const auto Filler =
+ Repetitor->Repeat(Instructions, InstrBenchmark.NumRepetitions);
+
+ object::OwningBinary<object::ObjectFile> ObjectFile;
+ if (DumpObjectToDisk) {
+ auto ObjectFilePath = writeObjectFile(BC, Filler);
+ if (Error E = ObjectFilePath.takeError()) {
+ InstrBenchmark.Error = toString(std::move(E));
+ return InstrBenchmark;
+ }
+ outs() << "Check generated assembly with: /usr/bin/objdump -d "
+ << *ObjectFilePath << "\n";
+ ObjectFile = getObjectFromFile(*ObjectFilePath);
+ } else {
+ SmallString<0> Buffer;
+ raw_svector_ostream OS(Buffer);
+ if (Error E = assembleToStream(
+ State.getExegesisTarget(), State.createTargetMachine(),
+ BC.LiveIns, BC.Key.RegisterInitialValues, Filler, OS)) {
+ return std::move(E);
+ }
+ ObjectFile = getObjectFromBuffer(OS.str());
+ }
- object::OwningBinary<object::ObjectFile> ObjectFile;
- if (DumpObjectToDisk) {
- auto ObjectFilePath = writeObjectFile(BC, Filler);
- if (Error E = ObjectFilePath.takeError()) {
+ const FunctionExecutorImpl Executor(State, std::move(ObjectFile),
+ Scratch.get());
+ auto NewMeasurements = runMeasurements(Executor);
+ if (Error E = NewMeasurements.takeError()) {
+ if (!E.isA<SnippetCrash>())
+ return std::move(E);
InstrBenchmark.Error = toString(std::move(E));
return InstrBenchmark;
}
- outs() << "Check generated assembly with: /usr/bin/objdump -d "
- << *ObjectFilePath << "\n";
- ObjectFile = getObjectFromFile(*ObjectFilePath);
- } else {
- SmallString<0> Buffer;
- raw_svector_ostream OS(Buffer);
- if (Error E = assembleToStream(State.getExegesisTarget(),
- State.createTargetMachine(), BC.LiveIns,
- BC.Key.RegisterInitialValues, Filler, OS)) {
- return std::move(E);
+ assert(InstrBenchmark.NumRepetitions > 0 && "invalid NumRepetitions");
+ for (BenchmarkMeasure &BM : *NewMeasurements) {
+ // Scale the measurements by instruction.
+ BM.PerInstructionValue /= InstrBenchmark.NumRepetitions;
+ // Scale the measurements by snippet.
+ BM.PerSnippetValue *= static_cast<double>(Instructions.size()) /
+ InstrBenchmark.NumRepetitions;
+ }
+ if (InstrBenchmark.Measurements.empty()) {
+ InstrBenchmark.Measurements = std::move(*NewMeasurements);
+ continue;
}
- ObjectFile = getObjectFromBuffer(OS.str());
- }
- const FunctionExecutorImpl Executor(State, std::move(ObjectFile),
- Scratch.get());
- auto Measurements = runMeasurements(Executor);
- if (Error E = Measurements.takeError()) {
- if (!E.isA<SnippetCrash>())
- return std::move(E);
- InstrBenchmark.Error = toString(std::move(E));
- return InstrBenchmark;
- }
- InstrBenchmark.Measurements = std::move(*Measurements);
- assert(InstrBenchmark.NumRepetitions > 0 && "invalid NumRepetitions");
- for (BenchmarkMeasure &BM : InstrBenchmark.Measurements) {
- // Scale the measurements by instruction.
- BM.PerInstructionValue /= InstrBenchmark.NumRepetitions;
- // Scale the measurements by snippet.
- BM.PerSnippetValue *= static_cast<double>(Instructions.size()) /
- InstrBenchmark.NumRepetitions;
+ assert(Repetitors.size() > 1 && !InstrBenchmark.Measurements.empty() &&
+ "We're in an 'min' repetition mode, and need to aggregate new "
+ "result to the existing result.");
+ assert(InstrBenchmark.Measurements.size() == NewMeasurements->size() &&
+ "Expected to have identical number of measurements.");
+ for (auto I : zip(InstrBenchmark.Measurements, *NewMeasurements)) {
+ BenchmarkMeasure &Measurement = std::get<0>(I);
+ BenchmarkMeasure &NewMeasurement = std::get<1>(I);
+ assert(Measurement.Key == NewMeasurement.Key &&
+ "Expected measurements to be symmetric");
+
+ Measurement.PerInstructionValue = std::min(
+ Measurement.PerInstructionValue, NewMeasurement.PerInstructionValue);
+ Measurement.PerSnippetValue =
+ std::min(Measurement.PerSnippetValue, NewMeasurement.PerSnippetValue);
+ }
}
+ // We successfully measured everything, so don't discard the results.
+ CBOR.disarm();
return InstrBenchmark;
}
static cl::opt<exegesis::InstructionBenchmark::RepetitionModeE> RepetitionMode(
"repetition-mode", cl::desc("how to repeat the instruction snippet"),
cl::cat(BenchmarkOptions),
- cl::values(clEnumValN(exegesis::InstructionBenchmark::Duplicate,
- "duplicate", "Duplicate the snippet"),
- clEnumValN(exegesis::InstructionBenchmark::Loop, "loop",
- "Loop over the snippet")));
+ cl::values(
+ clEnumValN(exegesis::InstructionBenchmark::Duplicate, "duplicate",
+ "Duplicate the snippet"),
+ clEnumValN(exegesis::InstructionBenchmark::Loop, "loop",
+ "Loop over the snippet"),
+ clEnumValN(exegesis::InstructionBenchmark::AggregateMin, "min",
+ "All of the above and take the minimum of measurements")),
+ cl::init(exegesis::InstructionBenchmark::Duplicate));
static cl::opt<unsigned>
NumRepetitions("num-repetitions",
const auto Opcodes = getOpcodesOrDie(State.getInstrInfo());
- const auto Repetitor = SnippetRepetitor::Create(RepetitionMode, State);
+ SmallVector<std::unique_ptr<const SnippetRepetitor>, 2> Repetitors;
+ if (RepetitionMode != InstructionBenchmark::RepetitionModeE::AggregateMin)
+ Repetitors.emplace_back(SnippetRepetitor::Create(RepetitionMode, State));
+ else {
+ for (InstructionBenchmark::RepetitionModeE RepMode :
+ {InstructionBenchmark::RepetitionModeE::Duplicate,
+ InstructionBenchmark::RepetitionModeE::Loop})
+ Repetitors.emplace_back(SnippetRepetitor::Create(RepMode, State));
+ }
+
+ BitVector AllReservedRegs;
+ llvm::for_each(Repetitors,
+ [&AllReservedRegs](
+ const std::unique_ptr<const SnippetRepetitor> &Repetitor) {
+ AllReservedRegs |= Repetitor->getReservedRegs();
+ });
std::vector<BenchmarkCode> Configurations;
if (!Opcodes.empty()) {
<< ": ignoring instruction without sched class\n";
continue;
}
- auto ConfigsForInstr =
- generateSnippets(State, Opcode, Repetitor->getReservedRegs());
+
+ auto ConfigsForInstr = generateSnippets(State, Opcode, AllReservedRegs);
if (!ConfigsForInstr) {
logAllUnhandledErrors(
ConfigsForInstr.takeError(), errs(),
for (const BenchmarkCode &Conf : Configurations) {
InstructionBenchmark Result = ExitOnErr(Runner->runConfiguration(
- Conf, NumRepetitions, *Repetitor, DumpObjectToDisk));
+ Conf, NumRepetitions, Repetitors, DumpObjectToDisk));
ExitOnFileError(BenchmarkFile, Result.writeYaml(State, BenchmarkFile));
}
exegesis::pfm::pfmTerminate();