summaryrefslogtreecommitdiffstats
path: root/llvm/tools/llvm-exegesis/lib/Latency.cpp
blob: 4a2632c7b5ceb09d08ca6c3cca3e236c14c5f921 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
//===-- 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
OpenPOWER on IntegriCloud