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