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 "llvm/ADT/StringExtras.h"
17 #include "llvm/ADT/StringRef.h"
18 #include "llvm/ADT/Twine.h"
19 #include "llvm/Support/FileSystem.h"
20 #include "llvm/Support/FormatVariadic.h"
21 #include "llvm/Support/MemoryBuffer.h"
22 #include "llvm/Support/Program.h"
23 
24 namespace exegesis {
25 
26 BenchmarkFailure::BenchmarkFailure(const llvm::Twine &S)
27     : llvm::StringError(S, llvm::inconvertibleErrorCode()) {}
28 
29 BenchmarkRunner::InstructionFilter::~InstructionFilter() = default;
30 
31 BenchmarkRunner::BenchmarkRunner(const LLVMState &State)
32     : State(State), MCInstrInfo(State.getInstrInfo()),
33       MCRegisterInfo(State.getRegInfo()),
34       RATC(MCRegisterInfo,
35            getFunctionReservedRegs(*State.createTargetMachine())) {}
36 
37 BenchmarkRunner::~BenchmarkRunner() = default;
38 
39 llvm::Expected<std::vector<InstructionBenchmark>>
40 BenchmarkRunner::run(unsigned Opcode, const InstructionFilter &Filter,
41                      unsigned NumRepetitions) {
42   // Ignore instructions that we cannot run.
43   if (State.getInstrInfo().get(Opcode).isPseudo())
44     return llvm::make_error<BenchmarkFailure>("Unsupported opcode: isPseudo");
45 
46   if (llvm::Error E = Filter.shouldRun(State, Opcode))
47     return std::move(E);
48 
49   llvm::Expected<std::vector<BenchmarkConfiguration>> ConfigurationOrError =
50       generateConfigurations(Opcode);
51 
52   if (llvm::Error E = ConfigurationOrError.takeError())
53     return std::move(E);
54 
55   std::vector<InstructionBenchmark> InstrBenchmarks;
56   for (const BenchmarkConfiguration &Conf : ConfigurationOrError.get())
57     InstrBenchmarks.push_back(runOne(Conf, Opcode, NumRepetitions));
58   return InstrBenchmarks;
59 }
60 
61 InstructionBenchmark
62 BenchmarkRunner::runOne(const BenchmarkConfiguration &Configuration,
63                         unsigned Opcode, unsigned NumRepetitions) const {
64   InstructionBenchmark InstrBenchmark;
65   InstrBenchmark.Mode = getMode();
66   InstrBenchmark.CpuName = State.getTargetMachine().getTargetCPU();
67   InstrBenchmark.LLVMTriple =
68       State.getTargetMachine().getTargetTriple().normalize();
69   InstrBenchmark.NumRepetitions = NumRepetitions;
70   InstrBenchmark.Info = Configuration.Info;
71 
72   const std::vector<llvm::MCInst> &Snippet = Configuration.Snippet;
73   if (Snippet.empty()) {
74     InstrBenchmark.Error = "Empty snippet";
75     return InstrBenchmark;
76   }
77 
78   InstrBenchmark.Key.Instructions = Snippet;
79 
80   // Repeat the snippet until there are at least NumInstructions in the
81   // resulting code. The snippet is always repeated at least once.
82   const auto GenerateInstructions = [&Snippet](const int MinInstructions) {
83     std::vector<llvm::MCInst> Code = Snippet;
84     for (int I = 0; I < MinInstructions; ++I)
85       Code.push_back(Snippet[I % Snippet.size()]);
86     return Code;
87   };
88 
89   // Assemble at least kMinInstructionsForSnippet instructions by repeating the
90   // snippet for debug/analysis. This is so that the user clearly understands
91   // that the inside instructions are repeated.
92   constexpr const int kMinInstructionsForSnippet = 16;
93   {
94     auto ObjectFilePath = writeObjectFile(
95         GenerateInstructions(kMinInstructionsForSnippet));
96     if (llvm::Error E = ObjectFilePath.takeError()) {
97       InstrBenchmark.Error = llvm::toString(std::move(E));
98       return InstrBenchmark;
99     }
100     const ExecutableFunction EF(State.createTargetMachine(),
101                               getObjectFromFile(*ObjectFilePath));
102     const auto FnBytes = EF.getFunctionBytes();
103     InstrBenchmark.AssembledSnippet.assign(FnBytes.begin(), FnBytes.end());
104   }
105 
106   // Assemble NumRepetitions instructions repetitions of the snippet for
107   // measurements.
108   auto ObjectFilePath = writeObjectFile(
109       GenerateInstructions(InstrBenchmark.NumRepetitions));
110   if (llvm::Error E = ObjectFilePath.takeError()) {
111     InstrBenchmark.Error = llvm::toString(std::move(E));
112     return InstrBenchmark;
113   }
114   llvm::outs() << "Check generated assembly with: /usr/bin/objdump -d "
115                << *ObjectFilePath << "\n";
116   const ExecutableFunction EF(State.createTargetMachine(),
117                             getObjectFromFile(*ObjectFilePath));
118   InstrBenchmark.Measurements = runMeasurements(EF, NumRepetitions);
119 
120   return InstrBenchmark;
121 }
122 
123 llvm::Expected<std::vector<BenchmarkConfiguration>>
124 BenchmarkRunner::generateConfigurations(unsigned Opcode) const {
125   if (auto E = generatePrototype(Opcode)) {
126     SnippetPrototype &Prototype = E.get();
127     // TODO: Generate as many configurations as needed here.
128     BenchmarkConfiguration Configuration;
129     Configuration.Info = Prototype.Explanation;
130     for (InstructionInstance &II : Prototype.Snippet)
131       Configuration.Snippet.push_back(II.randomizeUnsetVariablesAndBuild());
132     return std::vector<BenchmarkConfiguration>{Configuration};
133   } else
134     return E.takeError();
135 }
136 
137 llvm::Expected<std::string>
138 BenchmarkRunner::writeObjectFile(llvm::ArrayRef<llvm::MCInst> Code) const {
139   int ResultFD = 0;
140   llvm::SmallString<256> ResultPath;
141   if (llvm::Error E = llvm::errorCodeToError(llvm::sys::fs::createTemporaryFile(
142           "snippet", "o", ResultFD, ResultPath)))
143     return std::move(E);
144   llvm::raw_fd_ostream OFS(ResultFD, true /*ShouldClose*/);
145   assembleToStream(State.createTargetMachine(), Code, OFS);
146   return ResultPath.str();
147 }
148 
149 } // namespace exegesis
150