• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
BenchmarkFailure(const llvm::Twine & S)26 BenchmarkFailure::BenchmarkFailure(const llvm::Twine &S)
27     : llvm::StringError(S, llvm::inconvertibleErrorCode()) {}
28 
BenchmarkRunner(const LLVMState & State,InstructionBenchmark::ModeE Mode)29 BenchmarkRunner::BenchmarkRunner(const LLVMState &State,
30                                  InstructionBenchmark::ModeE Mode)
31     : State(State), RATC(State.getRegInfo(),
32                          getFunctionReservedRegs(State.getTargetMachine())),
33       Mode(Mode) {}
34 
35 BenchmarkRunner::~BenchmarkRunner() = default;
36 
37 llvm::Expected<std::vector<InstructionBenchmark>>
run(unsigned Opcode,unsigned NumRepetitions)38 BenchmarkRunner::run(unsigned Opcode, unsigned NumRepetitions) {
39   const llvm::MCInstrDesc &InstrDesc = State.getInstrInfo().get(Opcode);
40   // Ignore instructions that we cannot run.
41   if (InstrDesc.isPseudo())
42     return llvm::make_error<BenchmarkFailure>("Unsupported opcode: isPseudo");
43   if (InstrDesc.isBranch() || InstrDesc.isIndirectBranch())
44     return llvm::make_error<BenchmarkFailure>(
45         "Unsupported opcode: isBranch/isIndirectBranch");
46   if (InstrDesc.isCall() || InstrDesc.isReturn())
47     return llvm::make_error<BenchmarkFailure>(
48         "Unsupported opcode: isCall/isReturn");
49 
50   llvm::Expected<std::vector<BenchmarkConfiguration>> ConfigurationOrError =
51       generateConfigurations(Opcode);
52 
53   if (llvm::Error E = ConfigurationOrError.takeError())
54     return std::move(E);
55 
56   std::vector<InstructionBenchmark> InstrBenchmarks;
57   for (const BenchmarkConfiguration &Conf : ConfigurationOrError.get())
58     InstrBenchmarks.push_back(runOne(Conf, Opcode, NumRepetitions));
59   return InstrBenchmarks;
60 }
61 
62 InstructionBenchmark
runOne(const BenchmarkConfiguration & Configuration,unsigned Opcode,unsigned NumRepetitions) const63 BenchmarkRunner::runOne(const BenchmarkConfiguration &Configuration,
64                         unsigned Opcode, unsigned NumRepetitions) const {
65   InstructionBenchmark InstrBenchmark;
66   InstrBenchmark.Mode = Mode;
67   InstrBenchmark.CpuName = State.getTargetMachine().getTargetCPU();
68   InstrBenchmark.LLVMTriple =
69       State.getTargetMachine().getTargetTriple().normalize();
70   InstrBenchmark.NumRepetitions = NumRepetitions;
71   InstrBenchmark.Info = Configuration.Info;
72 
73   const std::vector<llvm::MCInst> &Snippet = Configuration.Snippet;
74   if (Snippet.empty()) {
75     InstrBenchmark.Error = "Empty snippet";
76     return InstrBenchmark;
77   }
78 
79   InstrBenchmark.Key.Instructions = Snippet;
80 
81   // Repeat the snippet until there are at least NumInstructions in the
82   // resulting code. The snippet is always repeated at least once.
83   const auto GenerateInstructions = [&Configuration](
84                                         const int MinInstructions) {
85     std::vector<llvm::MCInst> Code = Configuration.Snippet;
86     for (int I = 0; I < MinInstructions; ++I)
87       Code.push_back(Configuration.Snippet[I % Configuration.Snippet.size()]);
88     return Code;
89   };
90 
91   // Assemble at least kMinInstructionsForSnippet instructions by repeating the
92   // snippet for debug/analysis. This is so that the user clearly understands
93   // that the inside instructions are repeated.
94   constexpr const int kMinInstructionsForSnippet = 16;
95   {
96     auto ObjectFilePath =
97         writeObjectFile(Configuration.SnippetSetup,
98                         GenerateInstructions(kMinInstructionsForSnippet));
99     if (llvm::Error E = ObjectFilePath.takeError()) {
100       InstrBenchmark.Error = llvm::toString(std::move(E));
101       return InstrBenchmark;
102     }
103     const ExecutableFunction EF(State.createTargetMachine(),
104                                 getObjectFromFile(*ObjectFilePath));
105     const auto FnBytes = EF.getFunctionBytes();
106     InstrBenchmark.AssembledSnippet.assign(FnBytes.begin(), FnBytes.end());
107   }
108 
109   // Assemble NumRepetitions instructions repetitions of the snippet for
110   // measurements.
111   auto ObjectFilePath =
112       writeObjectFile(Configuration.SnippetSetup,
113                       GenerateInstructions(InstrBenchmark.NumRepetitions));
114   if (llvm::Error E = ObjectFilePath.takeError()) {
115     InstrBenchmark.Error = llvm::toString(std::move(E));
116     return InstrBenchmark;
117   }
118   llvm::outs() << "Check generated assembly with: /usr/bin/objdump -d "
119                << *ObjectFilePath << "\n";
120   const ExecutableFunction EF(State.createTargetMachine(),
121                               getObjectFromFile(*ObjectFilePath));
122   InstrBenchmark.Measurements = runMeasurements(EF, NumRepetitions);
123 
124   return InstrBenchmark;
125 }
126 
127 llvm::Expected<std::vector<BenchmarkConfiguration>>
generateConfigurations(unsigned Opcode) const128 BenchmarkRunner::generateConfigurations(unsigned Opcode) const {
129   if (auto E = generatePrototype(Opcode)) {
130     SnippetPrototype &Prototype = E.get();
131     // TODO: Generate as many configurations as needed here.
132     BenchmarkConfiguration Configuration;
133     Configuration.Info = Prototype.Explanation;
134     for (InstructionInstance &II : Prototype.Snippet) {
135       II.randomizeUnsetVariables();
136       Configuration.Snippet.push_back(II.build());
137     }
138     Configuration.SnippetSetup.RegsToDef = computeRegsToDef(Prototype.Snippet);
139     return std::vector<BenchmarkConfiguration>{Configuration};
140   } else
141     return E.takeError();
142 }
143 
computeRegsToDef(const std::vector<InstructionInstance> & Snippet) const144 std::vector<unsigned> BenchmarkRunner::computeRegsToDef(
145     const std::vector<InstructionInstance> &Snippet) const {
146   // Collect all register uses and create an assignment for each of them.
147   // Loop invariant: DefinedRegs[i] is true iif it has been set at least once
148   // before the current instruction.
149   llvm::BitVector DefinedRegs = RATC.emptyRegisters();
150   std::vector<unsigned> RegsToDef;
151   for (const InstructionInstance &II : Snippet) {
152     // Returns the register that this Operand sets or uses, or 0 if this is not
153     // a register.
154     const auto GetOpReg = [&II](const Operand &Op) -> unsigned {
155       if (Op.ImplicitReg) {
156         return *Op.ImplicitReg;
157       } else if (Op.IsExplicit && II.getValueFor(Op).isReg()) {
158         return II.getValueFor(Op).getReg();
159       }
160       return 0;
161     };
162     // Collect used registers that have never been def'ed.
163     for (const Operand &Op : II.Instr.Operands) {
164       if (!Op.IsDef) {
165         const unsigned Reg = GetOpReg(Op);
166         if (Reg > 0 && !DefinedRegs.test(Reg)) {
167           RegsToDef.push_back(Reg);
168           DefinedRegs.set(Reg);
169         }
170       }
171     }
172     // Mark defs as having been def'ed.
173     for (const Operand &Op : II.Instr.Operands) {
174       if (Op.IsDef) {
175         const unsigned Reg = GetOpReg(Op);
176         if (Reg > 0) {
177           DefinedRegs.set(Reg);
178         }
179       }
180     }
181   }
182   return RegsToDef;
183 }
184 
185 llvm::Expected<std::string>
writeObjectFile(const BenchmarkConfiguration::Setup & Setup,llvm::ArrayRef<llvm::MCInst> Code) const186 BenchmarkRunner::writeObjectFile(const BenchmarkConfiguration::Setup &Setup,
187                                  llvm::ArrayRef<llvm::MCInst> Code) const {
188   int ResultFD = 0;
189   llvm::SmallString<256> ResultPath;
190   if (llvm::Error E = llvm::errorCodeToError(llvm::sys::fs::createTemporaryFile(
191           "snippet", "o", ResultFD, ResultPath)))
192     return std::move(E);
193   llvm::raw_fd_ostream OFS(ResultFD, true /*ShouldClose*/);
194   assembleToStream(State.getExegesisTarget(), State.createTargetMachine(),
195                    Setup.RegsToDef, Code, OFS);
196   return ResultPath.str();
197 }
198 
199 llvm::Expected<SnippetPrototype>
generateSelfAliasingPrototype(const Instruction & Instr) const200 BenchmarkRunner::generateSelfAliasingPrototype(const Instruction &Instr) const {
201   const AliasingConfigurations SelfAliasing(Instr, Instr);
202   if (SelfAliasing.empty()) {
203     return llvm::make_error<BenchmarkFailure>("empty self aliasing");
204   }
205   SnippetPrototype Prototype;
206   InstructionInstance II(Instr);
207   if (SelfAliasing.hasImplicitAliasing()) {
208     Prototype.Explanation = "implicit Self cycles, picking random values.";
209   } else {
210     Prototype.Explanation =
211         "explicit self cycles, selecting one aliasing Conf.";
212     // This is a self aliasing instruction so defs and uses are from the same
213     // instance, hence twice II in the following call.
214     setRandomAliasing(SelfAliasing, II, II);
215   }
216   Prototype.Snippet.push_back(std::move(II));
217   return std::move(Prototype);
218 }
219 
220 llvm::Expected<SnippetPrototype>
generateUnconstrainedPrototype(const Instruction & Instr,llvm::StringRef Msg) const221 BenchmarkRunner::generateUnconstrainedPrototype(const Instruction &Instr,
222                                                 llvm::StringRef Msg) const {
223   SnippetPrototype Prototype;
224   Prototype.Explanation =
225       llvm::formatv("{0}, repeating an unconstrained assignment", Msg);
226   Prototype.Snippet.emplace_back(Instr);
227   return std::move(Prototype);
228 }
229 } // namespace exegesis
230