101 lines
3.8 KiB
C++
101 lines
3.8 KiB
C++
//===-- Latency.cpp ---------------------------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "Latency.h"
|
|
#include "BenchmarkResult.h"
|
|
#include "InstructionSnippetGenerator.h"
|
|
#include "PerfHelper.h"
|
|
#include "llvm/MC/MCInstrDesc.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include <algorithm>
|
|
#include <random>
|
|
|
|
namespace exegesis {
|
|
|
|
// FIXME: Handle memory, see PR36905.
|
|
static bool isInvalidOperand(const llvm::MCOperandInfo &OpInfo) {
|
|
switch (OpInfo.OperandType) {
|
|
default:
|
|
return true;
|
|
case llvm::MCOI::OPERAND_IMMEDIATE:
|
|
case llvm::MCOI::OPERAND_REGISTER:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static llvm::Error makeError(llvm::Twine Msg) {
|
|
return llvm::make_error<llvm::StringError>(Msg,
|
|
llvm::inconvertibleErrorCode());
|
|
}
|
|
|
|
LatencyBenchmarkRunner::~LatencyBenchmarkRunner() = default;
|
|
|
|
const char *LatencyBenchmarkRunner::getDisplayName() const { return "latency"; }
|
|
|
|
llvm::Expected<std::vector<llvm::MCInst>> LatencyBenchmarkRunner::createCode(
|
|
const LLVMState &State, const unsigned OpcodeIndex,
|
|
const unsigned NumRepetitions, const JitFunctionContext &Context) const {
|
|
std::default_random_engine RandomEngine;
|
|
const auto GetRandomIndex = [&RandomEngine](size_t Size) {
|
|
assert(Size > 0 && "trying to get select a random element of an empty set");
|
|
return std::uniform_int_distribution<>(0, Size - 1)(RandomEngine);
|
|
};
|
|
|
|
const auto &InstrInfo = State.getInstrInfo();
|
|
const auto &RegInfo = State.getRegInfo();
|
|
const llvm::MCInstrDesc &InstrDesc = InstrInfo.get(OpcodeIndex);
|
|
for (const llvm::MCOperandInfo &OpInfo : InstrDesc.operands()) {
|
|
if (isInvalidOperand(OpInfo))
|
|
return makeError("Only registers and immediates are supported");
|
|
}
|
|
|
|
const auto Vars = getVariables(RegInfo, InstrDesc, Context.getReservedRegs());
|
|
const std::vector<AssignmentChain> AssignmentChains =
|
|
computeSequentialAssignmentChains(RegInfo, Vars);
|
|
if (AssignmentChains.empty())
|
|
return makeError("Unable to find a dependency chain.");
|
|
const std::vector<llvm::MCPhysReg> Regs =
|
|
getRandomAssignment(Vars, AssignmentChains, GetRandomIndex);
|
|
const llvm::MCInst Inst = generateMCInst(InstrDesc, Vars, Regs);
|
|
if (!State.canAssemble(Inst))
|
|
return makeError("MCInst does not assemble.");
|
|
return std::vector<llvm::MCInst>(NumRepetitions, Inst);
|
|
}
|
|
|
|
std::vector<BenchmarkMeasure>
|
|
LatencyBenchmarkRunner::runMeasurements(const LLVMState &State,
|
|
const JitFunction &Function,
|
|
const unsigned NumRepetitions) const {
|
|
// Cycle measurements include some overhead from the kernel. Repeat the
|
|
// measure several times and take the minimum value.
|
|
constexpr const int NumMeasurements = 30;
|
|
int64_t MinLatency = std::numeric_limits<int64_t>::max();
|
|
const char *CounterName = State.getSubtargetInfo()
|
|
.getSchedModel()
|
|
.getExtraProcessorInfo()
|
|
.PfmCounters.CycleCounter;
|
|
if (!CounterName)
|
|
llvm::report_fatal_error("sched model does not define a cycle counter");
|
|
const pfm::PerfEvent CyclesPerfEvent(CounterName);
|
|
if (!CyclesPerfEvent.valid())
|
|
llvm::report_fatal_error("invalid perf event");
|
|
for (size_t I = 0; I < NumMeasurements; ++I) {
|
|
pfm::Counter Counter(CyclesPerfEvent);
|
|
Counter.start();
|
|
Function();
|
|
Counter.stop();
|
|
const int64_t Value = Counter.read();
|
|
if (Value < MinLatency)
|
|
MinLatency = Value;
|
|
}
|
|
return {{"latency", static_cast<double>(MinLatency) / NumRepetitions, ""}};
|
|
}
|
|
|
|
} // namespace exegesis
|