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::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>> 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 63 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>> 128 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 144 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> 186 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> 200 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> 221 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