Home | History | Annotate | Download | only in lib
      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