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