xref: /netbsd-src/external/apache2/llvm/dist/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp (revision ed75d7a867996c84cfa88e3b8906816277e957f7)
1 //===-- BenchmarkRunner.cpp -------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include <array>
10 #include <string>
11 
12 #include "Assembler.h"
13 #include "BenchmarkRunner.h"
14 #include "Error.h"
15 #include "MCInstrDescView.h"
16 #include "PerfHelper.h"
17 #include "llvm/ADT/StringExtras.h"
18 #include "llvm/ADT/StringRef.h"
19 #include "llvm/ADT/Twine.h"
20 #include "llvm/Support/CrashRecoveryContext.h"
21 #include "llvm/Support/FileSystem.h"
22 #include "llvm/Support/MemoryBuffer.h"
23 #include "llvm/Support/Program.h"
24 
25 namespace llvm {
26 namespace exegesis {
27 
28 BenchmarkRunner::BenchmarkRunner(const LLVMState &State,
29                                  InstructionBenchmark::ModeE Mode)
30     : State(State), Mode(Mode), Scratch(std::make_unique<ScratchSpace>()) {}
31 
32 BenchmarkRunner::~BenchmarkRunner() = default;
33 
34 namespace {
35 class FunctionExecutorImpl : public BenchmarkRunner::FunctionExecutor {
36 public:
37   FunctionExecutorImpl(const LLVMState &State,
38                        object::OwningBinary<object::ObjectFile> Obj,
39                        BenchmarkRunner::ScratchSpace *Scratch)
40       : Function(State.createTargetMachine(), std::move(Obj)),
41         Scratch(Scratch) {}
42 
43 private:
44   Expected<int64_t> runAndMeasure(const char *Counters) const override {
45     // We sum counts when there are several counters for a single ProcRes
46     // (e.g. P23 on SandyBridge).
47     int64_t CounterValue = 0;
48     SmallVector<StringRef, 2> CounterNames;
49     StringRef(Counters).split(CounterNames, '+');
50     char *const ScratchPtr = Scratch->ptr();
51     for (auto &CounterName : CounterNames) {
52       CounterName = CounterName.trim();
53       pfm::PerfEvent PerfEvent(CounterName);
54       if (!PerfEvent.valid())
55         report_fatal_error(
56             Twine("invalid perf event '").concat(CounterName).concat("'"));
57       pfm::Counter Counter(PerfEvent);
58       Scratch->clear();
59       {
60         CrashRecoveryContext CRC;
61         CrashRecoveryContext::Enable();
62         const bool Crashed = !CRC.RunSafely([this, &Counter, ScratchPtr]() {
63           Counter.start();
64           this->Function(ScratchPtr);
65           Counter.stop();
66         });
67         CrashRecoveryContext::Disable();
68         // FIXME: Better diagnosis.
69         if (Crashed)
70           return make_error<Failure>("snippet crashed while running");
71       }
72       CounterValue += Counter.read();
73     }
74     return CounterValue;
75   }
76 
77   const ExecutableFunction Function;
78   BenchmarkRunner::ScratchSpace *const Scratch;
79 };
80 } // namespace
81 
82 InstructionBenchmark BenchmarkRunner::runConfiguration(
83     const BenchmarkCode &BC, unsigned NumRepetitions,
84     const SnippetRepetitor &Repetitor, bool DumpObjectToDisk) const {
85   InstructionBenchmark InstrBenchmark;
86   InstrBenchmark.Mode = Mode;
87   InstrBenchmark.CpuName = State.getTargetMachine().getTargetCPU();
88   InstrBenchmark.LLVMTriple =
89       State.getTargetMachine().getTargetTriple().normalize();
90   InstrBenchmark.NumRepetitions = NumRepetitions;
91   InstrBenchmark.Info = BC.Info;
92 
93   const std::vector<MCInst> &Instructions = BC.Key.Instructions;
94 
95   InstrBenchmark.Key = BC.Key;
96 
97   // Assemble at least kMinInstructionsForSnippet instructions by repeating the
98   // snippet for debug/analysis. This is so that the user clearly understands
99   // that the inside instructions are repeated.
100   constexpr const int kMinInstructionsForSnippet = 16;
101   {
102     SmallString<0> Buffer;
103     raw_svector_ostream OS(Buffer);
104     assembleToStream(State.getExegesisTarget(), State.createTargetMachine(),
105                      BC.LiveIns, BC.Key.RegisterInitialValues,
106                      Repetitor.Repeat(Instructions, kMinInstructionsForSnippet),
107                      OS);
108     const ExecutableFunction EF(State.createTargetMachine(),
109                                 getObjectFromBuffer(OS.str()));
110     const auto FnBytes = EF.getFunctionBytes();
111     InstrBenchmark.AssembledSnippet.assign(FnBytes.begin(), FnBytes.end());
112   }
113 
114   // Assemble NumRepetitions instructions repetitions of the snippet for
115   // measurements.
116   const auto Filler =
117       Repetitor.Repeat(Instructions, InstrBenchmark.NumRepetitions);
118 
119   object::OwningBinary<object::ObjectFile> ObjectFile;
120   if (DumpObjectToDisk) {
121     auto ObjectFilePath = writeObjectFile(BC, Filler);
122     if (Error E = ObjectFilePath.takeError()) {
123       InstrBenchmark.Error = toString(std::move(E));
124       return InstrBenchmark;
125     }
126     outs() << "Check generated assembly with: /usr/bin/objdump -d "
127            << *ObjectFilePath << "\n";
128     ObjectFile = getObjectFromFile(*ObjectFilePath);
129   } else {
130     SmallString<0> Buffer;
131     raw_svector_ostream OS(Buffer);
132     assembleToStream(State.getExegesisTarget(), State.createTargetMachine(),
133                      BC.LiveIns, BC.Key.RegisterInitialValues, Filler, OS);
134     ObjectFile = getObjectFromBuffer(OS.str());
135   }
136 
137   const FunctionExecutorImpl Executor(State, std::move(ObjectFile),
138                                       Scratch.get());
139   auto Measurements = runMeasurements(Executor);
140   if (Error E = Measurements.takeError()) {
141     InstrBenchmark.Error = toString(std::move(E));
142     return InstrBenchmark;
143   }
144   InstrBenchmark.Measurements = std::move(*Measurements);
145   assert(InstrBenchmark.NumRepetitions > 0 && "invalid NumRepetitions");
146   for (BenchmarkMeasure &BM : InstrBenchmark.Measurements) {
147     // Scale the measurements by instruction.
148     BM.PerInstructionValue /= InstrBenchmark.NumRepetitions;
149     // Scale the measurements by snippet.
150     BM.PerSnippetValue *= static_cast<double>(Instructions.size()) /
151                           InstrBenchmark.NumRepetitions;
152   }
153 
154   return InstrBenchmark;
155 }
156 
157 Expected<std::string>
158 BenchmarkRunner::writeObjectFile(const BenchmarkCode &BC,
159                                  const FillFunction &FillFunction) const {
160   int ResultFD = 0;
161   SmallString<256> ResultPath;
162   if (Error E = errorCodeToError(
163           sys::fs::createTemporaryFile("snippet", "o", ResultFD, ResultPath)))
164     return std::move(E);
165   raw_fd_ostream OFS(ResultFD, true /*ShouldClose*/);
166   assembleToStream(State.getExegesisTarget(), State.createTargetMachine(),
167                    BC.LiveIns, BC.Key.RegisterInitialValues, FillFunction, OFS);
168   return ResultPath.str();
169 }
170 
171 BenchmarkRunner::FunctionExecutor::~FunctionExecutor() {}
172 
173 } // namespace exegesis
174 } // namespace llvm
175