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