llvm.org GIT mirror llvm / fd68be2
Re-land r329156 "Add llvm-exegesis tool." Fixed to depend on and initialize the native target instead of X86. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@329169 91177308-0d34-0410-b5e6-96231b3b80d8 Clement Courbet 1 year, 5 months ago
38 changed file(s) with 2963 addition(s) and 1 deletion(s). Raw diff Collapse all Expand all
8686 endif()
8787 check_library_exists(dl dlopen "" HAVE_LIBDL)
8888 check_library_exists(rt clock_gettime "" HAVE_LIBRT)
89 check_library_exists(pfm pfm_initialize "" HAVE_LIBPFM)
8990 endif()
9091
9192 if(HAVE_LIBPTHREAD)
5252 tblgen
5353 lit
5454 llvm-build
55 llvm-exegesis
5556 llvm-pdbutil
5657 llvm-readobj
0 llvm-exegesis - LLVM Machine Instruction Benchmark
1 ==================================================
2
3 SYNOPSIS
4 --------
5
6 :program:`llvm-exegesis` [*options*]
7
8 DESCRIPTION
9 -----------
10
11 :program:`llvm-exegesis` is a benchmarking tool that uses information available
12 in LLVM to measure host machine instruction characteristics like latency or port
13 decomposition.
14
15 Given an LLVM opcode name and a benchmarking mode, :program:`llvm-exegesis`
16 generates a code snippet that makes execution as serial (resp. as parallel) as
17 possible so that we can measure the latency (resp. uop decomposition) of the
18 instruction.
19 The code snippet is jitted and executed on the host subtarget. The time taken
20 (resp. resource usage) is measured using hardware performance counters. The
21 result is printed out as YAML to the standard output.
22
23 The main goal of this tool is to automatically (in)validate the LLVM's TableDef
24 scheduling models.
25
26 OPTIONS
27 -------
28
29 .. option:: -help
30
31 Print a summary of command line options.
32
33 .. option:: -opcode-index=
34
35 Specify the opcode to measure, by index.
36 Either `opcode-index` or `opcode-name` must be set.
37
38 .. option:: -opcode-name=
39
40 Specify the opcode to measure, by name.
41 Either `opcode-index` or `opcode-name` must be set.
42
43 .. option:: -benchmark-mode=[Latency|Uops]
44
45 Specify which characteristic of the opcode to measure.
46
47 .. option:: -num-repetitions=
48
49 Specify the number of repetitions of the asm snippet.
50 Higher values lead to more accurate measurements but lengthen the benchmark.
51
52
53 EXIT STATUS
54 -----------
55
56 :program:`llvm-exegesis` returns 0 on success. Otherwise, an error message is
57 printed to standard error, and the tool returns a non 0 value.
4646
4747 * Symbols starting with ``?`` are no longer mangled by LLVM when using the
4848 Windows ``x`` or ``w`` IR mangling schemes.
49
50 * A new tool named :doc:`llvm-exegesis ` has been
51 added. :program:`llvm-exegesis` automatically measures instruction scheduling
52 properties (latency/uops) and provides a principled way to edit scheduling
53 models.
4954
5055 * A new tool named :doc:`llvm-mca ` has been added.
5156 :program:`llvm-mca` is a static performance analysis tool that uses
9090 /* Define to 1 if you have the `edit' library (-ledit). */
9191 #cmakedefine HAVE_LIBEDIT ${HAVE_LIBEDIT}
9292
93 /* Define to 1 if you have the `pfm' library (-lpfm). */
94 #cmakedefine HAVE_LIBPFM ${HAVE_LIBPFM}
95
96 /* Define to 1 if you have the `psapi' library (-lpsapi). */
97 #cmakedefine HAVE_LIBPSAPI ${HAVE_LIBPSAPI}
98
9399 /* Define to 1 if you have the `pthread' library (-lpthread). */
94100 #cmakedefine HAVE_LIBPTHREAD ${HAVE_LIBPTHREAD}
95101
3131 llvm-dis
3232 llvm-dwarfdump
3333 llvm-dwp
34 llvm-exegesis
3435 llvm-extract
3536 llvm-jitlistener
3637 llvm-link
0 set(LLVM_LINK_COMPONENTS
1 Support
2 native
3 )
4
5 add_llvm_tool(llvm-exegesis
6 llvm-exegesis.cpp
7 )
8
9 add_subdirectory(lib)
10 target_link_libraries(llvm-exegesis PRIVATE LLVMExegesis)
11
12 if(HAVE_LIBPFM)
13 target_link_libraries(llvm-exegesis PRIVATE pfm)
14 endif()
15
0 ;===- ./tools/llvm-exegesis/LLVMBuild.txt ----------------------*- Conf -*--===;
1 ;
2 ; The LLVM Compiler Infrastructure
3 ;
4 ; This file is distributed under the University of Illinois Open Source
5 ; License. See LICENSE.TXT for details.
6 ;
7 ;===------------------------------------------------------------------------===;
8 ;
9 ; This is an LLVMBuild description file for the components in this subdirectory.
10 ;
11 ; For more information on the LLVMBuild system, please see:
12 ;
13 ; http://llvm.org/docs/LLVMBuild.html
14 ;
15 ;===------------------------------------------------------------------------===;
16
17 [component_0]
18 type = Tool
19 name = llvm-exegesis
20 parent = Tools
21 required_libraries = CodeGen ExecutionEngine MC MCJIT Native NativeCodeGen Object Support
0 //===-- BenchmarkResult.cpp -------------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "BenchmarkResult.h"
10 #include "llvm/ADT/StringRef.h"
11 #include "llvm/Support/FileOutputBuffer.h"
12 #include "llvm/Support/FileSystem.h"
13 #include "llvm/Support/Format.h"
14 #include "llvm/Support/raw_ostream.h"
15
16 // Defining YAML traits for IO.
17 namespace llvm {
18 namespace yaml {
19
20 // std::vector will be rendered as a list.
21 template <> struct SequenceElementTraits {
22 static const bool flow = false;
23 };
24
25 // exegesis::Measure is rendererd as a flow instead of a list.
26 // e.g. { "key": "the key", "value": 0123 }
27 template <> struct MappingTraits {
28 static void mapping(IO &Io, exegesis::BenchmarkMeasure &Obj) {
29 Io.mapRequired("key", Obj.Key);
30 Io.mapRequired("value", Obj.Value);
31 Io.mapOptional("debug_string", Obj.DebugString);
32 }
33 static const bool flow = true;
34 };
35
36 template <> struct MappingTraits {
37 static void mapping(IO &Io, exegesis::AsmTemplate &Obj) {
38 Io.mapRequired("name", Obj.Name);
39 }
40 };
41
42 template <> struct MappingTraits {
43 static void mapping(IO &Io, exegesis::InstructionBenchmark &Obj) {
44 Io.mapRequired("asm_template", Obj.AsmTmpl);
45 Io.mapRequired("cpu_name", Obj.CpuName);
46 Io.mapRequired("llvm_triple", Obj.LLVMTriple);
47 Io.mapRequired("num_repetitions", Obj.NumRepetitions);
48 Io.mapRequired("measurements", Obj.Measurements);
49 Io.mapRequired("error", Obj.Error);
50 }
51 };
52
53 } // namespace yaml
54 } // namespace llvm
55
56 namespace exegesis {
57
58 InstructionBenchmark
59 InstructionBenchmark::readYamlOrDie(llvm::StringRef Filename) {
60 std::unique_ptr MemBuffer = llvm::cantFail(
61 llvm::errorOrToExpected(llvm::MemoryBuffer::getFile(Filename)));
62 llvm::yaml::Input Yin(*MemBuffer);
63 InstructionBenchmark Benchmark;
64 Yin >> Benchmark;
65 return Benchmark;
66 }
67
68 void InstructionBenchmark::writeYamlOrDie(const llvm::StringRef Filename) {
69 if (Filename == "-") {
70 llvm::yaml::Output Yout(llvm::outs());
71 Yout << *this;
72 } else {
73 llvm::SmallString<1024> Buffer;
74 llvm::raw_svector_ostream Ostr(Buffer);
75 llvm::yaml::Output Yout(Ostr);
76 Yout << *this;
77 std::unique_ptr File =
78 llvm::cantFail(llvm::FileOutputBuffer::create(Filename, Buffer.size()));
79 memcpy(File->getBufferStart(), Buffer.data(), Buffer.size());
80 llvm::cantFail(File->commit());
81 }
82 }
83
84 } // namespace exegesis
0 //===-- BenchmarkResult.h ---------------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// Defines classes to represent measurements and serialize/deserialize them to
11 // Yaml.
12 ///
13 //===----------------------------------------------------------------------===//
14
15 #ifndef LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRESULT_H
16 #define LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRESULT_H
17
18 #include "llvm/ADT/StringRef.h"
19 #include "llvm/Support/YAMLTraits.h"
20 #include
21 #include
22
23 namespace exegesis {
24
25 struct AsmTemplate {
26 std::string Name;
27 };
28
29 struct BenchmarkMeasure {
30 std::string Key;
31 double Value;
32 std::string DebugString;
33 };
34
35 // The result of an instruction benchmark.
36 struct InstructionBenchmark {
37 AsmTemplate AsmTmpl;
38 std::string CpuName;
39 std::string LLVMTriple;
40 size_t NumRepetitions = 0;
41 std::vector Measurements;
42 std::string Error;
43
44 static InstructionBenchmark readYamlOrDie(llvm::StringRef Filename);
45
46 // Unfortunately this function is non const because of YAML traits.
47 void writeYamlOrDie(const llvm::StringRef Filename);
48 };
49
50 } // namespace exegesis
51
52 #endif // LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRESULT_H
0 //===-- BenchmarkRunner.cpp -------------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "BenchmarkRunner.h"
10 #include "InMemoryAssembler.h"
11 #include "llvm/ADT/StringExtras.h"
12 #include "llvm/ADT/StringRef.h"
13 #include "llvm/ADT/Twine.h"
14 #include
15
16 namespace exegesis {
17
18 BenchmarkRunner::InstructionFilter::~InstructionFilter() = default;
19
20 BenchmarkRunner::~BenchmarkRunner() = default;
21
22 InstructionBenchmark
23 BenchmarkRunner::run(const LLVMState &State, const unsigned Opcode,
24 unsigned NumRepetitions,
25 const InstructionFilter &Filter) const {
26 InstructionBenchmark InstrBenchmark;
27
28 InstrBenchmark.AsmTmpl.Name =
29 llvm::Twine(getDisplayName())
30 .concat(" ")
31 .concat(State.getInstrInfo().getName(Opcode))
32 .str();
33 InstrBenchmark.CpuName = State.getCpuName();
34 InstrBenchmark.LLVMTriple = State.getTriple();
35 InstrBenchmark.NumRepetitions = NumRepetitions;
36
37 // Ignore instructions that we cannot run.
38 if (State.getInstrInfo().get(Opcode).isPseudo()) {
39 InstrBenchmark.Error = "Unsupported opcode: isPseudo";
40 return InstrBenchmark;
41 }
42 if (llvm::Error E = Filter.shouldRun(State, Opcode)) {
43 InstrBenchmark.Error = llvm::toString(std::move(E));
44 return InstrBenchmark;
45 }
46
47 JitFunctionContext Context(State.createTargetMachine());
48 auto ExpectedInstructions =
49 createCode(State, Opcode, NumRepetitions, Context);
50 if (llvm::Error E = ExpectedInstructions.takeError()) {
51 InstrBenchmark.Error = llvm::toString(std::move(E));
52 return InstrBenchmark;
53 }
54
55 const std::vector Instructions = *ExpectedInstructions;
56 const JitFunction Function(std::move(Context), Instructions);
57 const llvm::StringRef CodeBytes = Function.getFunctionBytes();
58
59 std::string AsmExcerpt;
60 constexpr const int ExcerptSize = 100;
61 constexpr const int ExcerptTailSize = 10;
62 if (CodeBytes.size() <= ExcerptSize) {
63 AsmExcerpt = llvm::toHex(CodeBytes);
64 } else {
65 AsmExcerpt =
66 llvm::toHex(CodeBytes.take_front(ExcerptSize - ExcerptTailSize + 3));
67 AsmExcerpt += "...";
68 AsmExcerpt += llvm::toHex(CodeBytes.take_back(ExcerptTailSize));
69 }
70 llvm::outs() << "# Asm excerpt: " << AsmExcerpt << "\n";
71 llvm::outs().flush(); // In case we crash.
72
73 InstrBenchmark.Measurements =
74 runMeasurements(State, Function, NumRepetitions);
75 return InstrBenchmark;
76 }
77
78 } // namespace exegesis
0 //===-- BenchmarkRunner.h ---------------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// Defines the abstract BenchmarkRunner class for measuring a certain execution
11 /// property of instructions (e.g. latency).
12 ///
13 //===----------------------------------------------------------------------===//
14
15 #ifndef LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRUNNER_H
16 #define LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRUNNER_H
17
18 #include "BenchmarkResult.h"
19 #include "InMemoryAssembler.h"
20 #include "LlvmState.h"
21 #include "llvm/MC/MCInst.h"
22 #include "llvm/Support/Error.h"
23 #include
24
25 namespace exegesis {
26
27 // Common code for all benchmark modes.
28 class BenchmarkRunner {
29 public:
30 // Subtargets can disable running benchmarks for some instructions by
31 // returning an error here.
32 class InstructionFilter {
33 public:
34 virtual ~InstructionFilter();
35
36 virtual llvm::Error shouldRun(const LLVMState &State,
37 unsigned Opcode) const {
38 return llvm::ErrorSuccess();
39 }
40 };
41
42 virtual ~BenchmarkRunner();
43
44 InstructionBenchmark run(const LLVMState &State, unsigned Opcode,
45 unsigned NumRepetitions,
46 const InstructionFilter &Filter) const;
47
48 private:
49 virtual const char *getDisplayName() const = 0;
50
51 virtual llvm::Expected>
52 createCode(const LLVMState &State, unsigned OpcodeIndex,
53 unsigned NumRepetitions,
54 const JitFunctionContext &Context) const = 0;
55
56 virtual std::vector
57 runMeasurements(const LLVMState &State, const JitFunction &Function,
58 unsigned NumRepetitions) const = 0;
59 };
60
61 } // namespace exegesis
62
63 #endif // LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRUNNER_H
0 add_library(LLVMExegesis
1 STATIC
2 BenchmarkResult.cpp
3 BenchmarkRunner.cpp
4 InMemoryAssembler.cpp
5 InstructionSnippetGenerator.cpp
6 Latency.cpp
7 LlvmState.cpp
8 OperandGraph.cpp
9 PerfHelper.cpp
10 Uops.cpp
11 X86.cpp
12 )
13
14 llvm_update_compile_flags(LLVMExegesis)
15 llvm_map_components_to_libnames(libs
16 CodeGen
17 ExecutionEngine
18 MC
19 MCJIT
20 Support
21 )
22
23 target_link_libraries(LLVMExegesis ${libs})
24 set_target_properties(LLVMExegesis PROPERTIES FOLDER "Libraries")
0 //===-- InMemoryAssembler.cpp -----------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "InMemoryAssembler.h"
10 #include "llvm/ADT/SmallVector.h"
11 #include "llvm/ADT/StringRef.h"
12 #include "llvm/CodeGen/MachineInstrBuilder.h"
13 #include "llvm/CodeGen/MachineModuleInfo.h"
14 #include "llvm/CodeGen/MachineRegisterInfo.h"
15 #include "llvm/CodeGen/TargetInstrInfo.h"
16 #include "llvm/CodeGen/TargetPassConfig.h"
17 #include "llvm/ExecutionEngine/ExecutionEngine.h"
18 #include "llvm/ExecutionEngine/MCJIT.h"
19 #include "llvm/ExecutionEngine/SectionMemoryManager.h"
20 #include "llvm/IR/LLVMContext.h"
21 #include "llvm/IR/LegacyPassManager.h"
22 #include "llvm/MC/MCFixup.h"
23 #include "llvm/MC/MCInstrDesc.h"
24 #include "llvm/Object/Binary.h"
25 #include "llvm/Object/ObjectFile.h"
26 #include "llvm/PassInfo.h"
27 #include "llvm/PassRegistry.h"
28 #include "llvm/Support/raw_ostream.h"
29 #include "llvm/Target/TargetMachine.h"
30 #include "llvm/Target/TargetOptions.h"
31
32 namespace exegesis {
33
34 static constexpr const char ModuleID[] = "ExegesisInfoTest";
35 static constexpr const char FunctionID[] = "foo";
36
37 // Small utility function to add named passes.
38 static bool addPass(llvm::PassManagerBase &PM, llvm::StringRef PassName,
39 llvm::TargetPassConfig &TPC) {
40 const llvm::PassRegistry *PR = llvm::PassRegistry::getPassRegistry();
41 const llvm::PassInfo *PI = PR->getPassInfo(PassName);
42 if (!PI) {
43 llvm::errs() << " run-pass " << PassName << " is not registered.\n";
44 return true;
45 }
46
47 if (!PI->getNormalCtor()) {
48 llvm::errs() << " cannot create pass: " << PI->getPassName() << "\n";
49 return true;
50 }
51 llvm::Pass *P = PI->getNormalCtor()();
52 std::string Banner = std::string("After ") + std::string(P->getPassName());
53 PM.add(P);
54 TPC.printAndVerify(Banner);
55
56 return false;
57 }
58
59 // Creates a void MachineFunction with no argument.
60 static llvm::MachineFunction &
61 createVoidVoidMachineFunction(llvm::StringRef FunctionID, llvm::Module *Module,
62 llvm::MachineModuleInfo *MMI) {
63 llvm::Type *const ReturnType = llvm::Type::getInt32Ty(Module->getContext());
64 llvm::FunctionType *FunctionType = llvm::FunctionType::get(ReturnType, false);
65 llvm::Function *const F = llvm::Function::Create(
66 FunctionType, llvm::GlobalValue::InternalLinkage, FunctionID, Module);
67 // Making sure we can create a MachineFunction out of this Function even if it
68 // contains no IR.
69 F->setIsMaterializable(true);
70 return MMI->getOrCreateMachineFunction(*F);
71 }
72
73 static llvm::object::OwningBinary
74 assemble(llvm::Module *Module, std::unique_ptr MMI,
75 llvm::LLVMTargetMachine *LLVMTM) {
76 llvm::legacy::PassManager PM;
77 llvm::MCContext &Context = MMI->getContext();
78
79 llvm::TargetLibraryInfoImpl TLII(llvm::Triple(Module->getTargetTriple()));
80 PM.add(new llvm::TargetLibraryInfoWrapperPass(TLII));
81
82 llvm::TargetPassConfig *TPC = LLVMTM->createPassConfig(PM);
83 PM.add(TPC);
84 PM.add(MMI.release());
85 TPC->printAndVerify("MachineFunctionGenerator::assemble");
86 // Adding the following passes:
87 // - machineverifier: checks that the MachineFunction is well formed.
88 // - prologepilog: saves and restore callee saved registers.
89 for (const char *PassName : {"machineverifier", "prologepilog"})
90 if (addPass(PM, PassName, *TPC))
91 llvm::report_fatal_error("Unable to add a mandatory pass");
92 TPC->setInitialized();
93
94 llvm::SmallVector AsmBuffer;
95 llvm::raw_svector_ostream AsmStream(AsmBuffer);
96 // AsmPrinter is responsible for generating the assembly into AsmBuffer.
97 if (LLVMTM->addAsmPrinter(PM, AsmStream, llvm::TargetMachine::CGFT_ObjectFile,
98 Context))
99 llvm::report_fatal_error("Cannot add AsmPrinter passes");
100
101 PM.run(*Module); // Run all the passes
102
103 // Storing the generated assembly into a MemoryBuffer that owns the memory.
104 std::unique_ptr Buffer =
105 llvm::MemoryBuffer::getMemBufferCopy(AsmStream.str());
106 // Create the ObjectFile from the MemoryBuffer.
107 std::unique_ptr Obj = llvm::cantFail(
108 llvm::object::ObjectFile::createObjectFile(Buffer->getMemBufferRef()));
109 // Returning both the MemoryBuffer and the ObjectFile.
110 return llvm::object::OwningBinary(
111 std::move(Obj), std::move(Buffer));
112 }
113
114 static void fillMachineFunction(llvm::MachineFunction &MF,
115 llvm::ArrayRef Instructions) {
116 llvm::MachineBasicBlock *MBB = MF.CreateMachineBasicBlock();
117 MF.push_back(MBB);
118 const llvm::MCInstrInfo *MCII = MF.getTarget().getMCInstrInfo();
119 const llvm::DebugLoc DL;
120 for (const llvm::MCInst &Inst : Instructions) {
121 const unsigned Opcode = Inst.getOpcode();
122 const llvm::MCInstrDesc &MCID = MCII->get(Opcode);
123 llvm::MachineInstrBuilder Builder = llvm::BuildMI(MBB, DL, MCID);
124 for (unsigned OpIndex = 0, E = Inst.getNumOperands(); OpIndex < E;
125 ++OpIndex) {
126 const llvm::MCOperand &Op = Inst.getOperand(OpIndex);
127 if (Op.isReg()) {
128 const bool IsDef = OpIndex < MCID.getNumDefs();
129 unsigned Flags = 0;
130 const llvm::MCOperandInfo &OpInfo = MCID.operands().begin()[OpIndex];
131 if (IsDef && !OpInfo.isOptionalDef())
132 Flags |= llvm::RegState::Define;
133 Builder.addReg(Op.getReg(), Flags);
134 } else if (Op.isImm()) {
135 Builder.addImm(Op.getImm());
136 } else {
137 llvm_unreachable("Not yet implemented");
138 }
139 }
140 }
141 // Adding the Return Opcode.
142 const llvm::TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
143 llvm::BuildMI(MBB, DL, TII->get(TII->getReturnOpcode()));
144 }
145
146 namespace {
147
148 // Implementation of this class relies on the fact that a single object with a
149 // single function will be loaded into memory.
150 class TrackingSectionMemoryManager : public llvm::SectionMemoryManager {
151 public:
152 explicit TrackingSectionMemoryManager(uintptr_t *CodeSize)
153 : CodeSize(CodeSize) {}
154
155 uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment,
156 unsigned SectionID,
157 llvm::StringRef SectionName) override {
158 *CodeSize = Size;
159 return llvm::SectionMemoryManager::allocateCodeSection(
160 Size, Alignment, SectionID, SectionName);
161 }
162
163 private:
164 uintptr_t *const CodeSize = nullptr;
165 };
166
167 } // namespace
168
169 JitFunctionContext::JitFunctionContext(
170 std::unique_ptr TheTM)
171 : Context(llvm::make_unique()), TM(std::move(TheTM)),
172 MMI(llvm::make_unique(TM.get())),
173 Module(llvm::make_unique(ModuleID, *Context)) {
174 Module->setDataLayout(TM->createDataLayout());
175 MF = &createVoidVoidMachineFunction(FunctionID, Module.get(), MMI.get());
176 // We need to instruct the passes that we're done with SSA and virtual
177 // registers.
178 auto &Properties = MF->getProperties();
179 Properties.set(llvm::MachineFunctionProperties::Property::NoVRegs);
180 Properties.reset(llvm::MachineFunctionProperties::Property::IsSSA);
181 Properties.reset(llvm::MachineFunctionProperties::Property::TracksLiveness);
182 // prologue/epilogue pass needs the reserved registers to be frozen, this is
183 // usually done by the SelectionDAGISel pass.
184 MF->getRegInfo().freezeReservedRegs(*MF);
185 // Saving reserved registers for client.
186 ReservedRegs = MF->getSubtarget().getRegisterInfo()->getReservedRegs(*MF);
187 }
188
189 JitFunction::JitFunction(JitFunctionContext &&Context,
190 llvm::ArrayRef Instructions)
191 : FunctionContext(std::move(Context)) {
192 fillMachineFunction(*FunctionContext.MF, Instructions);
193 // We create the pass manager, run the passes and returns the produced
194 // ObjectFile.
195 llvm::object::OwningBinary ObjHolder =
196 assemble(FunctionContext.Module.get(), std::move(FunctionContext.MMI),
197 FunctionContext.TM.get());
198 assert(ObjHolder.getBinary() && "cannot create object file");
199 // Initializing the execution engine.
200 // We need to use the JIT EngineKind to be able to add an object file.
201 LLVMLinkInMCJIT();
202 uintptr_t CodeSize = 0;
203 std::string Error;
204 ExecEngine.reset(
205 llvm::EngineBuilder(std::move(FunctionContext.Module))
206 .setErrorStr(&Error)
207 .setMCPU(FunctionContext.TM->getTargetCPU())
208 .setEngineKind(llvm::EngineKind::JIT)
209 .setMCJITMemoryManager(
210 llvm::make_unique(&CodeSize))
211 .create(FunctionContext.TM.release()));
212 if (!ExecEngine)
213 llvm::report_fatal_error(Error);
214 // Adding the generated object file containing the assembled function.
215 // The ExecutionEngine makes sure the object file is copied into an
216 // executable page.
217 ExecEngine->addObjectFile(ObjHolder.takeBinary().first);
218 // Setting function
219 FunctionBytes =
220 llvm::StringRef(reinterpret_cast(
221 ExecEngine->getFunctionAddress(FunctionID)),
222 CodeSize);
223 }
224
225 } // namespace exegesis
0 //===-- InMemoryAssembler.h -------------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// Defines classes to assemble functions composed of a single basic block of
11 /// MCInsts.
12 ///
13 //===----------------------------------------------------------------------===//
14
15 #ifndef LLVM_TOOLS_LLVM_EXEGESIS_INMEMORYASSEMBLER_H
16 #define LLVM_TOOLS_LLVM_EXEGESIS_INMEMORYASSEMBLER_H
17
18 #include "llvm/ADT/BitVector.h"
19 #include "llvm/CodeGen/MachineFunction.h"
20 #include "llvm/CodeGen/MachineModuleInfo.h"
21 #include "llvm/CodeGen/TargetRegisterInfo.h"
22 #include "llvm/ExecutionEngine/ExecutionEngine.h"
23 #include "llvm/IR/LLVMContext.h"
24 #include "llvm/MC/MCInst.h"
25 #include
26 #include
27
28 namespace exegesis {
29
30 // Consumable context for JitFunction below.
31 // This temporary object allows for retrieving MachineFunction properties before
32 // assembling it.
33 class JitFunctionContext {
34 public:
35 explicit JitFunctionContext(std::unique_ptr TM);
36 // Movable
37 JitFunctionContext(JitFunctionContext &&) = default;
38 JitFunctionContext &operator=(JitFunctionContext &&) = default;
39 // Non copyable
40 JitFunctionContext(const JitFunctionContext &) = delete;
41 JitFunctionContext &operator=(const JitFunctionContext &) = delete;
42
43 const llvm::BitVector &getReservedRegs() const { return ReservedRegs; }
44
45 private:
46 friend class JitFunction;
47
48 std::unique_ptr Context;
49 std::unique_ptr TM;
50 std::unique_ptr MMI;
51 std::unique_ptr Module;
52 llvm::MachineFunction *MF = nullptr;
53 llvm::BitVector ReservedRegs;
54 };
55
56 // Creates a void() function from a sequence of llvm::MCInst.
57 class JitFunction {
58 public:
59 // Assembles Instructions into an executable function.
60 JitFunction(JitFunctionContext &&Context,
61 llvm::ArrayRef Instructions);
62
63 // Retrieves the function as an array of bytes.
64 llvm::StringRef getFunctionBytes() const { return FunctionBytes; }
65
66 // Retrieves the callable function.
67 void operator()() const { ((void (*)())FunctionBytes.data())(); }
68
69 private:
70 JitFunctionContext FunctionContext;
71 std::unique_ptr ExecEngine;
72 llvm::StringRef FunctionBytes;
73 };
74
75 } // namespace exegesis
76
77 #endif // LLVM_TOOLS_LLVM_EXEGESIS_INMEMORYASSEMBLER_H
0 //===-- InstructionSnippetGenerator.cpp -------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "InstructionSnippetGenerator.h"
10 #include "llvm/ADT/MapVector.h"
11 #include "llvm/ADT/STLExtras.h"
12 #include "llvm/ADT/SmallSet.h"
13 #include "llvm/MC/MCInstBuilder.h"
14 #include
15 #include
16 #include
17
18 namespace exegesis {
19
20 void Variable::print(llvm::raw_ostream &OS,
21 const llvm::MCRegisterInfo *RegInfo) const {
22 OS << "IsUse=" << IsUse << " IsDef=" << IsDef << " possible regs: {";
23 for (const size_t Reg : PossibleRegisters) {
24 if (RegInfo)
25 OS << RegInfo->getName(Reg);
26 else
27 OS << Reg;
28 OS << ",";
29 }
30 OS << "} ";
31 if (ExplicitOperands.empty()) {
32 OS << "implicit";
33 } else {
34 OS << "explicit ops: {";
35 for (const size_t Op : ExplicitOperands)
36 OS << Op << ",";
37 OS << "}";
38 }
39 OS << "\n";
40 }
41
42 // Update the state of a Variable with an explicit operand.
43 static void updateExplicitOperandVariable(const llvm::MCRegisterInfo &RegInfo,
44 const llvm::MCInstrDesc &InstrInfo,
45 const size_t OpIndex,
46 const llvm::BitVector &ReservedRegs,
47 Variable &Var) {
48 const bool IsDef = OpIndex < InstrInfo.getNumDefs();
49 if (IsDef)
50 Var.IsDef = true;
51 if (!IsDef)
52 Var.IsUse = true;
53 Var.ExplicitOperands.push_back(OpIndex);
54 const llvm::MCOperandInfo &OpInfo = InstrInfo.opInfo_begin()[OpIndex];
55 if (OpInfo.RegClass >= 0) {
56 Var.IsReg = true;
57 for (const llvm::MCPhysReg &Reg : RegInfo.getRegClass(OpInfo.RegClass)) {
58 if (!ReservedRegs[Reg])
59 Var.PossibleRegisters.insert(Reg);
60 }
61 }
62 }
63
64 static Variable &findVariableWithOperand(llvm::SmallVector &Vars,
65 size_t OpIndex) {
66 // Vars.size() is small (<10) so a linear scan is good enough.
67 for (Variable &Var : Vars) {
68 if (llvm::is_contained(Var.ExplicitOperands, OpIndex))
69 return Var;
70 }
71 assert(false && "Illegal state");
72 static Variable *const EmptyVariable = new Variable();
73 return *EmptyVariable;
74 }
75
76 llvm::SmallVector
77 getVariables(const llvm::MCRegisterInfo &RegInfo,
78 const llvm::MCInstrDesc &InstrInfo,
79 const llvm::BitVector &ReservedRegs) {
80 llvm::SmallVector Vars;
81 // For each operand, its "tied to" operand or -1.
82 llvm::SmallVector TiedToMap;
83 for (size_t I = 0, E = InstrInfo.getNumOperands(); I < E; ++I) {
84 TiedToMap.push_back(InstrInfo.getOperandConstraint(I, llvm::MCOI::TIED_TO));
85 }
86 // Adding non tied operands.
87 for (size_t I = 0, E = InstrInfo.getNumOperands(); I < E; ++I) {
88 if (TiedToMap[I] >= 0)
89 continue; // dropping tied ones.
90 Vars.emplace_back();
91 updateExplicitOperandVariable(RegInfo, InstrInfo, I, ReservedRegs,
92 Vars.back());
93 }
94 // Adding tied operands to existing variables.
95 for (size_t I = 0, E = InstrInfo.getNumOperands(); I < E; ++I) {
96 if (TiedToMap[I] < 0)
97 continue; // dropping non-tied ones.
98 updateExplicitOperandVariable(RegInfo, InstrInfo, I, ReservedRegs,
99 findVariableWithOperand(Vars, TiedToMap[I]));
100 }
101 // Adding implicit defs.
102 for (size_t I = 0, E = InstrInfo.getNumImplicitDefs(); I < E; ++I) {
103 Vars.emplace_back();
104 Variable &Var = Vars.back();
105 const llvm::MCPhysReg Reg = InstrInfo.getImplicitDefs()[I];
106 assert(!ReservedRegs[Reg] && "implicit def of reserved register");
107 Var.PossibleRegisters.insert(Reg);
108 Var.IsDef = true;
109 Var.IsReg = true;
110 }
111 // Adding implicit uses.
112 for (size_t I = 0, E = InstrInfo.getNumImplicitUses(); I < E; ++I) {
113 Vars.emplace_back();
114 Variable &Var = Vars.back();
115 const llvm::MCPhysReg Reg = InstrInfo.getImplicitUses()[I];
116 assert(!ReservedRegs[Reg] && "implicit use of reserved register");
117 Var.PossibleRegisters.insert(Reg);
118 Var.IsUse = true;
119 Var.IsReg = true;
120 }
121
122 return Vars;
123 }
124
125 VariableAssignment::VariableAssignment(size_t VarIdx,
126 llvm::MCPhysReg AssignedReg)
127 : VarIdx(VarIdx), AssignedReg(AssignedReg) {}
128
129 bool VariableAssignment::operator==(const VariableAssignment &Other) const {
130 return std::tie(VarIdx, AssignedReg) ==
131 std::tie(Other.VarIdx, Other.AssignedReg);
132 }
133
134 bool VariableAssignment::operator<(const VariableAssignment &Other) const {
135 return std::tie(VarIdx, AssignedReg) <
136 std::tie(Other.VarIdx, Other.AssignedReg);
137 }
138
139 void dumpAssignmentChain(const llvm::MCRegisterInfo &RegInfo,
140 const AssignmentChain &Chain) {
141 for (const VariableAssignment &Assignment : Chain) {
142 llvm::outs() << llvm::format("(%d %s) ", Assignment.VarIdx,
143 RegInfo.getName(Assignment.AssignedReg));
144 }
145 llvm::outs() << "\n";
146 }
147
148 std::vector
149 computeSequentialAssignmentChains(const llvm::MCRegisterInfo &RegInfo,
150 llvm::ArrayRef Vars) {
151 using graph::Node;
152 graph::Graph Graph;
153
154 // Add register aliasing to the graph.
155 setupRegisterAliasing(RegInfo, Graph);
156
157 // Adding variables to the graph.
158 for (size_t I = 0, E = Vars.size(); I < E; ++I) {
159 const Variable &Var = Vars[I];
160 const Node N = Node::Var(I);
161 if (Var.IsDef) {
162 Graph.connect(Node::In(), N);
163 for (const size_t Reg : Var.PossibleRegisters)
164 Graph.connect(N, Node::Reg(Reg));
165 }
166 if (Var.IsUse) {
167 Graph.connect(N, Node::Out());
168 for (const size_t Reg : Var.PossibleRegisters)
169 Graph.connect(Node::Reg(Reg), N);
170 }
171 }
172
173 // Find all possible dependency chains (aka all possible paths from In to Out
174 // node).
175 std::vector AllChains;
176 for (;;) {
177 const auto Path = Graph.getPathFrom(Node::In(), Node::Out());
178 if (Path.empty())
179 break;
180 switch (Path.size()) {
181 case 0:
182 case 1:
183 case 2:
184 case 4:
185 assert(false && "Illegal state");
186 break;
187 case 3: { // IN -> variable -> OUT
188 const size_t VarIdx = Path[1].varValue();
189 for (size_t Reg : Vars[VarIdx].PossibleRegisters) {
190 AllChains.emplace_back();
191 AllChains.back().emplace(VarIdx, Reg);
192 }
193 Graph.disconnect(Path[0], Path[1]); // IN -> variable
194 Graph.disconnect(Path[1], Path[2]); // variable -> OUT
195 break;
196 }
197 default: { // IN -> var1 -> Reg[...] -> var2 -> OUT
198 const size_t Last = Path.size() - 1;
199 const size_t Var1 = Path[1].varValue();
200 const llvm::MCPhysReg Reg1 = Path[2].regValue();
201 const llvm::MCPhysReg Reg2 = Path[Last - 2].regValue();
202 const size_t Var2 = Path[Last - 1].varValue();
203 AllChains.emplace_back();
204 AllChains.back().emplace(Var1, Reg1);
205 AllChains.back().emplace(Var2, Reg2);
206 Graph.disconnect(Path[1], Path[2]); // Var1 -> Reg[0]
207 break;
208 }
209 }
210 }
211
212 return AllChains;
213 }
214
215 std::vector
216 getRandomAssignment(llvm::ArrayRef Vars,
217 llvm::ArrayRef Chains,
218 const std::function &RandomIndexForSize) {
219 // Registers are initialized with 0 (aka NoRegister).
220 std::vector Registers(Vars.size(), 0);
221 if (Chains.empty())
222 return Registers;
223 // Pick one of the chains and set Registers that are fully constrained (have
224 // no degrees of freedom).
225 const size_t ChainIndex = RandomIndexForSize(Chains.size());
226 for (const VariableAssignment Assignment : Chains[ChainIndex])
227 Registers[Assignment.VarIdx] = Assignment.AssignedReg;
228 // Registers with remaining degrees of freedom are assigned randomly.
229 for (size_t I = 0, E = Vars.size(); I < E; ++I) {
230 llvm::MCPhysReg &Reg = Registers[I];
231 const Variable &Var = Vars[I];
232 const auto &PossibleRegisters = Var.PossibleRegisters;
233 if (Reg > 0 || PossibleRegisters.empty())
234 continue;
235 Reg = PossibleRegisters[RandomIndexForSize(PossibleRegisters.size())];
236 }
237 return Registers;
238 }
239
240 // Finds a matching register `reg` for variable `VarIdx` and sets
241 // `RegAssignments[r]` to `VarIdx`. Returns false if no matching can be found.
242 // `seen.count(r)` is 1 if register `reg` has been processed.
243 static bool findMatchingRegister(
244 llvm::ArrayRef Vars, const size_t VarIdx,
245 std::unordered_set &Seen,
246 std::unordered_map &RegAssignments) {
247 for (const llvm::MCPhysReg Reg : Vars[VarIdx].PossibleRegisters) {
248 if (!Seen.count(Reg)) {
249 Seen.insert(Reg); // Mark `Reg` as seen.
250 // If `Reg` is not assigned to a variable, or if `Reg` was assigned to a
251 // variable which has an alternate possible register, assign `Reg` to
252 // variable `VarIdx`. Since `Reg` is marked as assigned in the above line,
253 // `RegAssignments[r]` in the following recursive call will not get
254 // assigned `Reg` again.
255 const auto AssignedVarIt = RegAssignments.find(Reg);
256 if (AssignedVarIt == RegAssignments.end() ||
257 findMatchingRegister(Vars, AssignedVarIt->second, Seen,
258 RegAssignments)) {
259 RegAssignments[Reg] = VarIdx;
260 return true;
261 }
262 }
263 }
264 return false;
265 }
266
267 // This is actually a maximum bipartite matching problem:
268 // https://en.wikipedia.org/wiki/Matching_(graph_theory)#Bipartite_matching
269 // The graph has variables on the left and registers on the right, with an edge
270 // between variable `I` and register `Reg` iff
271 // `Vars[I].PossibleRegisters.count(A)`.
272 // Note that a greedy approach won't work for cases like:
273 // Vars[0] PossibleRegisters={C,B}
274 // Vars[1] PossibleRegisters={A,B}
275 // Vars[2] PossibleRegisters={A,C}
276 // There is a feasible solution {0->B, 1->A, 2->C}, but the greedy solution is
277 // {0->C, 1->A, oops}.
278 std::vector
279 getExclusiveAssignment(llvm::ArrayRef Vars) {
280 // `RegAssignments[r]` is the variable id that was assigned register `Reg`.
281 std::unordered_map RegAssignments;
282
283 for (size_t VarIdx = 0, E = Vars.size(); VarIdx < E; ++VarIdx) {
284 if (!Vars[VarIdx].IsReg)
285 continue;
286 std::unordered_set Seen;
287 if (!findMatchingRegister(Vars, VarIdx, Seen, RegAssignments))
288 return {}; // Infeasible.
289 }
290
291 std::vector Registers(Vars.size(), 0);
292 for (const auto &RegVarIdx : RegAssignments)
293 Registers[RegVarIdx.second] = RegVarIdx.first;
294 return Registers;
295 }
296
297 std::vector
298 getGreedyAssignment(llvm::ArrayRef Vars) {
299 std::vector Registers(Vars.size(), 0);
300 llvm::SmallSet Assigned;
301 for (size_t VarIdx = 0, E = Vars.size(); VarIdx < E; ++VarIdx) {
302 const auto &Var = Vars[VarIdx];
303 if (!Var.IsReg)
304 continue;
305 if (Var.PossibleRegisters.empty())
306 return {};
307 // Try possible registers until an unassigned one is found.
308 for (const auto Reg : Var.PossibleRegisters) {
309 if (Assigned.insert(Reg).second) {
310 Registers[VarIdx] = Reg;
311 break;
312 }
313 }
314 // Fallback to first possible register.
315 if (Registers[VarIdx] == 0)
316 Registers[VarIdx] = Var.PossibleRegisters[0];
317 }
318 return Registers;
319 }
320
321 llvm::MCInst generateMCInst(const llvm::MCInstrDesc &InstrInfo,
322 llvm::ArrayRef Vars,
323 llvm::ArrayRef VarRegs) {
324 const size_t NumOperands = InstrInfo.getNumOperands();
325 llvm::SmallVector OperandToRegister(NumOperands, 0);
326
327 // We browse the variable and for each explicit operands we set the selected
328 // register in the OperandToRegister array.
329 for (size_t I = 0, E = Vars.size(); I < E; ++I) {
330 for (const size_t OpIndex : Vars[I].ExplicitOperands) {
331 OperandToRegister[OpIndex] = VarRegs[I];
332 }
333 }
334
335 // Building the instruction.
336 llvm::MCInstBuilder Builder(InstrInfo.getOpcode());
337 for (size_t I = 0, E = InstrInfo.getNumOperands(); I < E; ++I) {
338 const llvm::MCOperandInfo &OpInfo = InstrInfo.opInfo_begin()[I];
339 switch (OpInfo.OperandType) {
340 case llvm::MCOI::OperandType::OPERAND_REGISTER:
341 Builder.addReg(OperandToRegister[I]);
342 break;
343 case llvm::MCOI::OperandType::OPERAND_IMMEDIATE:
344 Builder.addImm(1);
345 break;
346 default:
347 Builder.addOperand(llvm::MCOperand());
348 }
349 }
350
351 return Builder;
352 }
353
354 } // namespace exegesis
0 //===-- InstructionSnippetGenerator.h ---------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// Defines helper classes to generate code snippets, in particular register
11 /// assignment.
12 ///
13 //===----------------------------------------------------------------------===//
14
15 #ifndef LLVM_TOOLS_LLVM_EXEGESIS_INSTRUCTIONSNIPPETGENERATOR_H
16 #define LLVM_TOOLS_LLVM_EXEGESIS_INSTRUCTIONSNIPPETGENERATOR_H
17
18 #include "OperandGraph.h"
19 #include "llvm/ADT/BitVector.h"
20 #include "llvm/ADT/SetVector.h"
21 #include "llvm/ADT/SmallVector.h"
22 #include "llvm/MC/MCInst.h"
23 #include "llvm/MC/MCInstrDesc.h"
24 #include "llvm/MC/MCRegisterInfo.h"
25 #include
26
27 namespace exegesis {
28
29 // A Variable represents a set of possible values that we need to choose from.
30 // It may represent one or more explicit operands that are tied together, or one
31 // implicit operand.
32 class Variable final {
33 public:
34 bool IsUse = false;
35 bool IsDef = false;
36 bool IsReg = false;
37
38 // Lists all the explicit operand indices that are tied to this variable.
39 // Empty if Variable represents an implicit operand.
40 llvm::SmallVector ExplicitOperands;
41
42 // - In case of explicit operands, PossibleRegisters is the expansion of the
43 // operands's RegClass registers. Please note that tied together explicit
44 // operands share the same RegClass.
45 // - In case of implicit operands, PossibleRegisters is a singleton MCPhysReg.
46 llvm::SmallSetVector PossibleRegisters;
47
48 // If RegInfo is null, register names won't get resolved.
49 void print(llvm::raw_ostream &OS, const llvm::MCRegisterInfo *RegInfo) const;
50 };
51
52 // Builds a model of implicit and explicit operands for InstrDesc into
53 // Variables.
54 llvm::SmallVector
55 getVariables(const llvm::MCRegisterInfo &RegInfo,
56 const llvm::MCInstrDesc &InstrDesc,
57 const llvm::BitVector &ReservedRegs);
58
59 // A simple object to represent a Variable assignement.
60 struct VariableAssignment {
61 VariableAssignment(size_t VarIdx, llvm::MCPhysReg AssignedReg);
62
63 size_t VarIdx;
64 llvm::MCPhysReg AssignedReg;
65
66 bool operator==(const VariableAssignment &) const;
67 bool operator<(const VariableAssignment &) const;
68 };
69
70 // An AssignmentChain is a set of assignement realizing a dependency chain.
71 // We inherit from std::set to leverage uniqueness of elements.
72 using AssignmentChain = std::set;
73
74 // Debug function to print an assignment chain.
75 void dumpAssignmentChain(const llvm::MCRegisterInfo &RegInfo,
76 const AssignmentChain &Chain);
77
78 // Inserts Variables into a graph representing register aliasing and finds all
79 // the possible dependency chains for this instruction, i.e. all the possible
80 // assignement of operands that would make execution of the instruction
81 // sequential.
82 std::vector
83 computeSequentialAssignmentChains(const llvm::MCRegisterInfo &RegInfo,
84 llvm::ArrayRef Vars);
85
86 // Selects a random configuration leading to a dependency chain.
87 // The result is a vector of the same size as `Vars`.
88 // `random_index_for_size` is a functor giving a random value in [0, arg[.
89 std::vector
90 getRandomAssignment(llvm::ArrayRef Vars,
91 llvm::ArrayRef Chains,
92 const std::function &RandomIndexForSize);
93
94 // Finds an assignment of registers to variables such that no two variables are
95 // assigned the same register.
96 // The result is a vector of the same size as `Vars`, or `{}` if the
97 // assignment is not feasible.
98 std::vector
99 getExclusiveAssignment(llvm::ArrayRef Vars);
100
101 // Finds a greedy assignment of registers to variables. Each variable gets
102 // assigned the first possible register that is not already assigned to a
103 // previous variable. If there is no such register, the variable gets assigned
104 // the first possible register.
105 // The result is a vector of the same size as `Vars`, or `{}` if the
106 // assignment is not feasible.
107 std::vector
108 getGreedyAssignment(llvm::ArrayRef Vars);
109
110 // Generates an LLVM MCInst with the previously computed variables.
111 // Immediate values are set to 1.
112 llvm::MCInst generateMCInst(const llvm::MCInstrDesc &InstrDesc,
113 llvm::ArrayRef Vars,
114 llvm::ArrayRef VarRegs);
115
116 } // namespace exegesis
117
118 #endif // LLVM_TOOLS_LLVM_EXEGESIS_INSTRUCTIONSNIPPETGENERATOR_H
0 ;===- ./tools/llvm-exegesis/lib/LLVMBuild.txt ------------------*- Conf -*--===;
1 ;
2 ; The LLVM Compiler Infrastructure
3 ;
4 ; This file is distributed under the University of Illinois Open Source
5 ; License. See LICENSE.TXT for details.
6 ;
7 ;===------------------------------------------------------------------------===;
8 ;
9 ; This is an LLVMBuild description file for the components in this subdirectory.
10 ;
11 ; For more information on the LLVMBuild system, please see:
12 ;
13 ; http://llvm.org/docs/LLVMBuild.html
14 ;
15 ;===------------------------------------------------------------------------===;
16
17 [component_0]
18 type = Library
19 name = Exegesis
20 parent = Libraries
21 required_libraries = CodeGen ExecutionEngine MC MCJIT Object Support
0 //===-- Latency.cpp ---------------------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "Latency.h"
10 #include "BenchmarkResult.h"
11 #include "InstructionSnippetGenerator.h"
12 #include "PerfHelper.h"
13 #include "llvm/MC/MCInstrDesc.h"
14 #include "llvm/Support/Error.h"
15 #include
16 #include
17
18 namespace exegesis {
19
20 // FIXME: Handle memory, see PR36905.
21 static bool isInvalidOperand(const llvm::MCOperandInfo &OpInfo) {
22 switch (OpInfo.OperandType) {
23 default:
24 return true;
25 case llvm::MCOI::OPERAND_IMMEDIATE:
26 case llvm::MCOI::OPERAND_REGISTER:
27 return false;
28 }
29 }
30
31 static llvm::Error makeError(llvm::Twine Msg) {
32 return llvm::make_error(Msg,
33 llvm::inconvertibleErrorCode());
34 }
35
36 LatencyBenchmarkRunner::~LatencyBenchmarkRunner() = default;
37
38 const char *LatencyBenchmarkRunner::getDisplayName() const { return "latency"; }
39
40 llvm::Expected> LatencyBenchmarkRunner::createCode(
41 const LLVMState &State, const unsigned OpcodeIndex,
42 const unsigned NumRepetitions, const JitFunctionContext &Context) const {
43 std::default_random_engine RandomEngine;
44 const auto GetRandomIndex = [&RandomEngine](size_t Size) {
45 assert(Size > 0 && "trying to get select a random element of an empty set");
46 return std::uniform_int_distribution<>(0, Size - 1)(RandomEngine);
47 };
48
49 const auto &InstrInfo = State.getInstrInfo();
50 const auto &RegInfo = State.getRegInfo();
51 const llvm::MCInstrDesc &InstrDesc = InstrInfo.get(OpcodeIndex);
52 for (const llvm::MCOperandInfo &OpInfo : InstrDesc.operands()) {
53 if (isInvalidOperand(OpInfo))
54 return makeError("Only registers and immediates are supported");
55 }
56
57 const auto Vars = getVariables(RegInfo, InstrDesc, Context.getReservedRegs());
58 const std::vector AssignmentChains =
59 computeSequentialAssignmentChains(RegInfo, Vars);
60 if (AssignmentChains.empty())
61 return makeError("Unable to find a dependency chain.");
62 const std::vector Regs =
63 getRandomAssignment(Vars, AssignmentChains, GetRandomIndex);
64 const llvm::MCInst Inst = generateMCInst(InstrDesc, Vars, Regs);
65 if (!State.canAssemble(Inst))
66 return makeError("MCInst does not assemble.");
67 return std::vector(NumRepetitions, Inst);
68 }
69
70 std::vector
71 LatencyBenchmarkRunner::runMeasurements(const LLVMState &State,
72 const JitFunction &Function,
73 const unsigned NumRepetitions) const {
74 // Cycle measurements include some overhead from the kernel. Repeat the
75 // measure several times and take the minimum value.
76 constexpr const int NumMeasurements = 30;
77 int64_t MinLatency = std::numeric_limits::max();
78 // FIXME: Read the perf event from the MCSchedModel (see PR36984).
79 const pfm::PerfEvent CyclesPerfEvent("UNHALTED_CORE_CYCLES");
80 if (!CyclesPerfEvent.valid())
81 llvm::report_fatal_error("invalid perf event 'UNHALTED_CORE_CYCLES'");
82 for (size_t I = 0; I < NumMeasurements; ++I) {
83 pfm::Counter Counter(CyclesPerfEvent);
84 Counter.start();
85 Function();
86 Counter.stop();
87 const int64_t Value = Counter.read();
88 if (Value < MinLatency)
89 MinLatency = Value;
90 }
91 return {{"latency", static_cast(MinLatency) / NumRepetitions}};
92 }
93
94 } // namespace exegesis
0 //===-- Latency.h -----------------------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// A BenchmarkRunner implementation to measure instruction latencies.
11 ///
12 //===----------------------------------------------------------------------===//
13
14 #ifndef LLVM_TOOLS_LLVM_EXEGESIS_LATENCY_H
15 #define LLVM_TOOLS_LLVM_EXEGESIS_LATENCY_H
16
17 #include "BenchmarkRunner.h"
18
19 namespace exegesis {
20
21 class LatencyBenchmarkRunner : public BenchmarkRunner {
22 public:
23 ~LatencyBenchmarkRunner() override;
24
25 private:
26 const char *getDisplayName() const override;
27
28 llvm::Expected>
29 createCode(const LLVMState &State, unsigned OpcodeIndex,
30 unsigned NumRepetitions,
31 const JitFunctionContext &Context) const override;
32
33 std::vector
34 runMeasurements(const LLVMState &State, const JitFunction &Function,
35 unsigned NumRepetitions) const override;
36 };
37
38 } // namespace exegesis
39
40 #endif // LLVM_TOOLS_LLVM_EXEGESIS_LATENCY_H
0 //===-- LlvmState.cpp -------------------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "LlvmState.h"
10 #include "llvm/ADT/SmallVector.h"
11 #include "llvm/MC/MCCodeEmitter.h"
12 #include "llvm/MC/MCContext.h"
13 #include "llvm/MC/MCFixup.h"
14 #include "llvm/MC/MCObjectFileInfo.h"
15 #include "llvm/Support/TargetRegistry.h"
16 #include "llvm/Support/raw_ostream.h"
17 #include "llvm/Target/TargetMachine.h"
18 #include "llvm/Target/TargetOptions.h"
19
20 namespace exegesis {
21
22 LLVMState::LLVMState()
23 : TheTriple(llvm::sys::getProcessTriple()),
24 CpuName(llvm::sys::getHostCPUName().str()) {
25 std::string Error;
26 TheTarget = llvm::TargetRegistry::lookupTarget(TheTriple, Error);
27 assert(TheTarget && "unknown target for host");
28 SubtargetInfo.reset(
29 TheTarget->createMCSubtargetInfo(TheTriple, CpuName, Features));
30 InstrInfo.reset(TheTarget->createMCInstrInfo());
31 RegInfo.reset(TheTarget->createMCRegInfo(TheTriple));
32 AsmInfo.reset(TheTarget->createMCAsmInfo(*RegInfo, TheTriple));
33 }
34
35 std::unique_ptr
36 LLVMState::createTargetMachine() const {
37 const llvm::TargetOptions Options;
38 return std::unique_ptr(
39 static_cast(TheTarget->createTargetMachine(
40 TheTriple, CpuName, Features, Options, llvm::Reloc::Model::Static)));
41 }
42
43 bool LLVMState::canAssemble(const llvm::MCInst &Inst) const {
44 llvm::MCObjectFileInfo ObjectFileInfo;
45 llvm::MCContext Context(AsmInfo.get(), RegInfo.get(), &ObjectFileInfo);
46 std::unique_ptr CodeEmitter(
47 TheTarget->createMCCodeEmitter(*InstrInfo, *RegInfo, Context));
48 llvm::SmallVector Tmp;
49 llvm::raw_svector_ostream OS(Tmp);
50 llvm::SmallVector Fixups;
51 CodeEmitter->encodeInstruction(Inst, OS, Fixups, *SubtargetInfo);
52 return Tmp.size() > 0;
53 }
54
55 } // namespace exegesis
0 //===-- LlvmState.h ---------------------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #ifndef LLVM_TOOLS_LLVM_EXEGESIS_LLVMSTATE_H
10 #define LLVM_TOOLS_LLVM_EXEGESIS_LLVMSTATE_H
11
12 #include "llvm/MC/MCAsmInfo.h"
13 #include "llvm/MC/MCInst.h"
14 #include "llvm/MC/MCInstrInfo.h"
15 #include "llvm/MC/MCRegisterInfo.h"
16 #include "llvm/MC/MCSubtargetInfo.h"
17 #include "llvm/Target/TargetMachine.h"
18 #include
19 #include
20
21 namespace exegesis {
22
23 // An object to initialize LLVM and prepare objects needed to run the
24 // measurements.
25 class LLVMState {
26 public:
27 LLVMState();
28
29 llvm::StringRef getTriple() const { return TheTriple; }
30 llvm::StringRef getCpuName() const { return CpuName; }
31 llvm::StringRef getFeatures() const { return Features; }
32
33 const llvm::MCInstrInfo &getInstrInfo() const { return *InstrInfo; }
34
35 const llvm::MCRegisterInfo &getRegInfo() const { return *RegInfo; }
36
37 const llvm::MCSubtargetInfo &getSubtargetInfo() const {
38 return *SubtargetInfo;
39 }
40
41 std::unique_ptr createTargetMachine() const;
42
43 bool canAssemble(const llvm::MCInst &mc_inst) const;
44
45 private:
46 std::string TheTriple;
47 std::string CpuName;
48 std::string Features;
49 const llvm::Target *TheTarget = nullptr;
50 std::unique_ptr SubtargetInfo;
51 std::unique_ptr InstrInfo;
52 std::unique_ptr RegInfo;
53 std::unique_ptr AsmInfo;
54 };
55
56 } // namespace exegesis
57
58 #endif // LLVM_TOOLS_LLVM_EXEGESIS_LLVMSTATE_H
0 //===-- OperandGraph.cpp ----------------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "OperandGraph.h"
10 #include "llvm/MC/MCRegisterInfo.h"
11
12 namespace exegesis {
13 namespace graph {
14
15 void Node::dump(const llvm::MCRegisterInfo &RegInfo) const {
16 switch (type()) {
17 case NodeType::VARIABLE:
18 printf(" %d", varValue());
19 break;
20 case NodeType::REG:
21 printf(" %s", RegInfo.getName(regValue()));
22 break;
23 case NodeType::IN:
24 printf(" IN");
25 break;
26 case NodeType::OUT:
27 printf(" OUT");
28 break;
29 }
30 }
31
32 NodeType Node::type() const { return first; }
33
34 int Node::regValue() const {
35 assert(first == NodeType::REG && "regValue() called on non-reg");
36 return second;
37 }
38
39 int Node::varValue() const {
40 assert(first == NodeType::VARIABLE && "varValue() called on non-var");
41 return second;
42 }
43
44 void Graph::connect(const Node From, const Node To) {
45 AdjacencyLists[From].insert(To);
46 }
47
48 void Graph::disconnect(const Node From, const Node To) {
49 AdjacencyLists[From].erase(To);
50 }
51
52 std::vector Graph::getPathFrom(const Node From, const Node To) const {
53 std::vector Path;
54 NodeSet Seen;
55 dfs(From, To, Path, Seen);
56 return Path;
57 }
58
59 // DFS is implemented recursively, this is fine as graph size is small (~250
60 // nodes, ~200 edges, longuest path depth < 10).
61 bool Graph::dfs(const Node Current, const Node Sentinel,
62 std::vector &Path, NodeSet &Seen) const {
63 Path.push_back(Current);
64 Seen.insert(Current);
65 if (Current == Sentinel)
66 return true;
67 if (AdjacencyLists.count(Current)) {
68 for (const Node Next : AdjacencyLists.find(Current)->second) {
69 if (Seen.count(Next))
70 continue;
71 if (dfs(Next, Sentinel, Path, Seen))
72 return true;
73 }
74 }
75 Path.pop_back();
76 return false;
77 }
78
79 // For each Register Units we walk up their parents.
80 // Let's take the case of the A register family:
81 //
82 // RAX
83 // ^
84 // EAX
85 // ^
86 // AX
87 // ^ ^
88 // AH AL
89 //
90 // Register Units are AH and AL.
91 // Walking them up gives the following lists:
92 // AH->AX->EAX->RAX and AL->AX->EAX->RAX
93 // When walking the lists we add connect current to parent both ways leading to
94 // the following connections:
95 //
96 // AL<->AX, AH<->AX, AX<->EAX, EAX<->RAX
97 // We repeat this process for all Unit Registers to cover all connections.
98 void setupRegisterAliasing(const llvm::MCRegisterInfo &RegInfo,
99 Graph &TheGraph) {
100 using SuperItr = llvm::MCSuperRegIterator;
101 for (size_t Reg = 0, E = RegInfo.getNumRegUnits(); Reg < E; ++Reg) {
102 size_t Current = Reg;
103 for (SuperItr Super(Reg, &RegInfo); Super.isValid(); ++Super) {
104 const Node A = Node::Reg(Current);
105 const Node B = Node::Reg(*Super);
106 TheGraph.connect(A, B);
107 TheGraph.connect(B, A);
108 Current = *Super;
109 }
110 }
111 }
112
113 } // namespace graph
114 } // namespace exegesis
0 //===-- OperandGraph.h ------------------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// A collection of tools to model register aliasing and instruction operand.
11 /// This is used to find an aliasing between the input and output registers of
12 /// an instruction. It allows us to repeat an instruction and make sure that
13 /// successive instances are executed sequentially.
14 ///
15 //===----------------------------------------------------------------------===//
16
17 #ifndef LLVM_TOOLS_LLVM_EXEGESIS_OPERANDGRAPH_H
18 #define LLVM_TOOLS_LLVM_EXEGESIS_OPERANDGRAPH_H
19
20 #include "llvm/MC/MCRegisterInfo.h"
21 #include
22 #include
23 #include
24 #include
25
26 namespace exegesis {
27 namespace graph {
28
29 enum class NodeType {
30 VARIABLE, // An set of "tied together operands" to resolve.
31 REG, // A particular register.
32 IN, // The input node.
33 OUT // The output node.
34 };
35
36 // A Node in the graph, it has a type and an int value.
37 struct Node : public std::pair {
38 using std::pair::pair;
39
40 static Node Reg(int Value) { return {NodeType::REG, Value}; }
41 static Node Var(int Value) { return {NodeType::VARIABLE, Value}; }
42 static Node In() { return {NodeType::IN, 0}; }
43 static Node Out() { return {NodeType::OUT, 0}; }
44
45 NodeType type() const;
46 int regValue() const; // checks that type==REG and returns value.
47 int varValue() const; // checks that type==VARIABLE and returns value.
48
49 void dump(const llvm::MCRegisterInfo &RegInfo) const;
50 };
51
52 // Graph represents the connectivity of registers for a particular instruction.
53 // This object is used to select registers that would create a dependency chain
54 // between instruction's input and output.
55 struct Graph {
56 public:
57 void connect(const Node From, const Node To);
58 void disconnect(const Node From, const Node To);
59
60 // Tries to find a path between 'From' and 'To' nodes.
61 // Returns empty if no path is found.
62 std::vector getPathFrom(const Node From, const Node To) const;
63
64 private:
65 // We use std::set to keep the implementation simple, using an unordered_set
66 // requires the definition of a hasher.
67 using NodeSet = std::set;
68
69 // Performs a Depth First Search from 'current' node up until 'sentinel' node
70 // is found. 'path' is the recording of the traversed nodes, 'seen' is the
71 // collection of nodes seen so far.
72 bool dfs(const Node Current, const Node Sentinel, std::vector &Path,
73 NodeSet &Seen) const;
74
75 // We use std::map to keep the implementation simple, using an unordered_map
76 // requires the definition of a hasher.
77 std::map AdjacencyLists;
78 };
79
80 // Add register nodes to graph and connect them when they alias. Connection is
81 // both ways.
82 void setupRegisterAliasing(const llvm::MCRegisterInfo &RegInfo,
83 Graph &TheGraph);
84
85 } // namespace graph
86 } // namespace exegesis
87
88 #endif // LLVM_TOOLS_LLVM_EXEGESIS_OPERANDGRAPH_H
0 //===-- PerfHelper.cpp ------------------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "PerfHelper.h"
10 #include "llvm/Config/config.h"
11 #include "llvm/Support/raw_ostream.h"
12 #ifdef HAVE_LIBPFM
13 #include "perfmon/perf_event.h"
14 #include "perfmon/pfmlib.h"
15 #include "perfmon/pfmlib_perf_event.h"
16 #endif
17
18 namespace exegesis {
19 namespace pfm {
20
21 #ifdef HAVE_LIBPFM
22 static bool isPfmError(int Code) { return Code != PFM_SUCCESS; }
23 #endif
24
25 bool pfmInitialize() {
26 #ifdef HAVE_LIBPFM
27 return isPfmError(pfm_initialize());
28 #else
29 return true;
30 #endif
31 }
32
33 void pfmTerminate() {
34 #ifdef HAVE_LIBPFM
35 pfm_terminate();
36 #endif
37 }
38
39 PerfEvent::~PerfEvent() {
40 #ifdef HAVE_LIBPFM
41 delete Attr;
42 ;
43 #endif
44 }
45
46 PerfEvent::PerfEvent(PerfEvent &&Other)
47 : EventString(std::move(Other.EventString)),
48 FullQualifiedEventString(std::move(Other.FullQualifiedEventString)),
49 Attr(Other.Attr) {
50 Other.Attr = nullptr;
51 }
52
53 PerfEvent::PerfEvent(llvm::StringRef PfmEventString)
54 : EventString(PfmEventString.str()), Attr(nullptr) {
55 #ifdef HAVE_LIBPFM
56 char *Fstr = nullptr;
57 pfm_perf_encode_arg_t Arg = {};
58 Attr = new perf_event_attr();
59 Arg.attr = Attr;
60 Arg.fstr = &Fstr;
61 Arg.size = sizeof(pfm_perf_encode_arg_t);
62 const int Result = pfm_get_os_event_encoding(EventString.c_str(), PFM_PLM3,
63 PFM_OS_PERF_EVENT, &Arg);
64 if (isPfmError(Result)) {
65 // We don't know beforehand which counters are available (e.g. 6 uops ports
66 // on Sandybridge but 8 on Haswell) so we report the missing counter without
67 // crashing.
68 llvm::errs() << pfm_strerror(Result) << " - cannot create event "
69 << EventString;
70 }
71 if (Fstr) {
72 FullQualifiedEventString = Fstr;
73 free(Fstr);
74 }
75 #endif
76 }
77
78 llvm::StringRef PerfEvent::name() const { return EventString; }
79
80 bool PerfEvent::valid() const { return !FullQualifiedEventString.empty(); }
81
82 const perf_event_attr *PerfEvent::attribute() const { return Attr; }
83
84 llvm::StringRef PerfEvent::getPfmEventString() const {
85 return FullQualifiedEventString;
86 }
87
88 #ifdef HAVE_LIBPFM
89 Counter::Counter(const PerfEvent &Event) {
90 const pid_t Pid = 0; // measure current process/thread.
91 const int Cpu = -1; // measure any processor.
92 const int GroupFd = -1; // no grouping of counters.
93 const uint32_t Flags = 0;
94 perf_event_attr AttrCopy = *Event.attribute();
95 FileDescriptor = perf_event_open(&AttrCopy, Pid, Cpu, GroupFd, Flags);
96 assert(FileDescriptor != -1 &&
97 "Unable to open event, make sure your kernel allows user space perf "
98 "monitoring.");
99 }
100
101 Counter::~Counter() { close(FileDescriptor); }
102
103 void Counter::start() { ioctl(FileDescriptor, PERF_EVENT_IOC_RESET, 0); }
104
105 void Counter::stop() { ioctl(FileDescriptor, PERF_EVENT_IOC_DISABLE, 0); }
106
107 int64_t Counter::read() const {
108 int64_t Count = 0;
109 ::read(FileDescriptor, &Count, sizeof(Count));
110 return Count;
111 }
112
113 #else
114
115 Counter::Counter(const PerfEvent &Event) : FileDescriptor(-1) {}
116
117 Counter::~Counter() = default;
118
119 void Counter::start() {}
120
121 void Counter::stop() {}
122
123 int64_t Counter::read() const { return 42; }
124
125 #endif
126
127 } // namespace pfm
128 } // namespace exegesis
0 //===-- PerfHelper.h ------------------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// Helpers for measuring perf events.
11 ///
12 //===----------------------------------------------------------------------===//
13
14 #ifndef LLVM_TOOLS_LLVM_EXEGESIS_PERFHELPER_H
15 #define LLVM_TOOLS_LLVM_EXEGESIS_PERFHELPER_H
16
17 #include "llvm/ADT/ArrayRef.h"
18 #include "llvm/ADT/StringRef.h"
19 #include
20 #include
21
22 struct perf_event_attr;
23
24 namespace exegesis {
25 namespace pfm {
26
27 // Returns true on error.
28 bool pfmInitialize();
29 void pfmTerminate();
30
31 // Retrieves the encoding for the event described by pfm_event_string.
32 // NOTE: pfm_initialize() must be called before creating PerfEvent objects.
33 class PerfEvent {
34 public:
35 // http://perfmon2.sourceforge.net/manv4/libpfm.html
36 // Events are expressed as strings. e.g. "INSTRUCTION_RETIRED"
37 explicit PerfEvent(llvm::StringRef pfm_event_string);
38
39 PerfEvent(const PerfEvent &) = delete;
40 PerfEvent(PerfEvent &&other);
41 ~PerfEvent();
42
43 // The pfm_event_string passed at construction time.
44 llvm::StringRef name() const;
45
46 // Whether the event was successfully created.
47 bool valid() const;
48
49 // The encoded event to be passed to the Kernel.
50 const perf_event_attr *attribute() const;
51
52 // The fully qualified name for the event.
53 // e.g. "snb_ep::INSTRUCTION_RETIRED:e=0:i=0:c=0:t=0:u=1:k=0:mg=0:mh=1"
54 llvm::StringRef getPfmEventString() const;
55
56 private:
57 const std::string EventString;
58 std::string FullQualifiedEventString;
59 perf_event_attr *Attr;
60 };
61
62 // Uses a valid PerfEvent to configure the Kernel so we can measure the
63 // underlying event.
64 struct Counter {
65 // event: the PerfEvent to measure.
66 explicit Counter(const PerfEvent &event);
67
68 Counter(const Counter &) = delete;
69 Counter(Counter &&other) = default;
70
71 ~Counter();
72
73 void start(); // Starts the measurement of the event.
74 void stop(); // Stops the measurement of the event.
75 int64_t read() const; // Return the current value of the counter.
76
77 private:
78 int FileDescriptor = -1;
79 };
80
81 // Helper to measure a list of PerfEvent for a particular function.
82 // callback is called for each successful measure (PerfEvent needs to be valid).
83 template
84 void Measure(
85 llvm::ArrayRef Events,
86 const std::function &Callback,
87 Function Fn) {
88 for (const auto &Event : Events) {
89 if (!Event.valid())
90 continue;
91 Counter Cnt(Event);
92 Cnt.start();
93 Fn();
94 Cnt.stop();
95 Callback(Event, Cnt.read());
96 }
97 }
98
99 } // namespace pfm
100 } // namespace exegesis
101
102 #endif // LLVM_TOOLS_LLVM_EXEGESIS_PERFHELPER_H
0 //===-- Uops.cpp ------------------------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "Uops.h"
10 #include "BenchmarkResult.h"
11 #include "InstructionSnippetGenerator.h"
12 #include "PerfHelper.h"
13 #include "llvm/ADT/StringExtras.h"
14 #include "llvm/MC/MCInstrDesc.h"
15 #include "llvm/MC/MCSchedule.h"
16 #include "llvm/Support/Error.h"
17 #include
18 #include
19 #include
20 #include
21
22 namespace exegesis {
23
24 // FIXME: Handle memory (see PR36906)
25 static bool isInvalidOperand(const llvm::MCOperandInfo &OpInfo) {
26 switch (OpInfo.OperandType) {
27 default:
28 return true;
29 case llvm::MCOI::OPERAND_IMMEDIATE:
30 case llvm::MCOI::OPERAND_REGISTER:
31 return false;
32 }
33 }
34
35 static llvm::Error makeError(llvm::Twine Msg) {
36 return llvm::make_error(Msg,
37 llvm::inconvertibleErrorCode());
38 }
39
40 // FIXME: Read the counter names from the ProcResourceUnits when PR36984 is
41 // fixed.
42 static const std::string *getEventNameFromProcResName(const char *ProcResName) {
43 static const std::unordered_map Entries = {
44 {"SBPort0", "UOPS_DISPATCHED_PORT:PORT_0"},
45 {"SBPort1", "UOPS_DISPATCHED_PORT:PORT_1"},
46 {"SBPort4", "UOPS_DISPATCHED_PORT:PORT_4"},
47 {"SBPort5", "UOPS_DISPATCHED_PORT:PORT_5"},
48 {"HWPort0", "UOPS_DISPATCHED_PORT:PORT_0"},
49 {"HWPort1", "UOPS_DISPATCHED_PORT:PORT_1"},
50 {"HWPort2", "UOPS_DISPATCHED_PORT:PORT_2"},
51 {"HWPort3", "UOPS_DISPATCHED_PORT:PORT_3"},
52 {"HWPort4", "UOPS_DISPATCHED_PORT:PORT_4"},
53 {"HWPort5", "UOPS_DISPATCHED_PORT:PORT_5"},
54 {"HWPort6", "UOPS_DISPATCHED_PORT:PORT_6"},
55 {"HWPort7", "UOPS_DISPATCHED_PORT:PORT_7"},
56 {"SKLPort0", "UOPS_DISPATCHED_PORT:PORT_0"},
57 {"SKLPort1", "UOPS_DISPATCHED_PORT:PORT_1"},
58 {"SKLPort2", "UOPS_DISPATCHED_PORT:PORT_2"},
59 {"SKLPort3", "UOPS_DISPATCHED_PORT:PORT_3"},
60 {"SKLPort4", "UOPS_DISPATCHED_PORT:PORT_4"},
61 {"SKLPort5", "UOPS_DISPATCHED_PORT:PORT_5"},
62 {"SKLPort6", "UOPS_DISPATCHED_PORT:PORT_6"},
63 {"SKXPort7", "UOPS_DISPATCHED_PORT:PORT_7"},
64 {"SKXPort0", "UOPS_DISPATCHED_PORT:PORT_0"},
65 {"SKXPort1", "UOPS_DISPATCHED_PORT:PORT_1"},
66 {"SKXPort2", "UOPS_DISPATCHED_PORT:PORT_2"},
67 {"SKXPort3", "UOPS_DISPATCHED_PORT:PORT_3"},
68 {"SKXPort4", "UOPS_DISPATCHED_PORT:PORT_4"},
69 {"SKXPort5", "UOPS_DISPATCHED_PORT:PORT_5"},
70 {"SKXPort6", "UOPS_DISPATCHED_PORT:PORT_6"},
71 {"SKXPort7", "UOPS_DISPATCHED_PORT:PORT_7"},
72 };
73 const auto It = Entries.find(ProcResName);
74 return It == Entries.end() ? nullptr : &It->second;
75 }
76
77 static std::vector generateIndependentAssignments(
78 const LLVMState &State, const llvm::MCInstrDesc &InstrDesc,
79 llvm::SmallVector Vars, int MaxAssignments) {
80 std::unordered_set IsUsedByAnyVar;
81 for (const Variable &Var : Vars) {
82 if (Var.IsUse) {
83 IsUsedByAnyVar.insert(Var.PossibleRegisters.begin(),
84 Var.PossibleRegisters.end());
85 }
86 }
87
88 std::vector Pattern;
89 for (int A = 0; A < MaxAssignments; ++A) {
90 // FIXME: This is a bit pessimistic. We should get away with an
91 // assignment that ensures that the set of assigned registers for uses and
92 // the set of assigned registers for defs do not intersect (registers
93 // for uses (resp defs) do not have to be all distinct).
94 const std::vector Regs = getExclusiveAssignment(Vars);
95 if (Regs.empty())
96 break;
97 // Remove all assigned registers defs that are used by at least one other
98 // variable from the list of possible variable registers. This ensures that
99 // we never create a RAW hazard that would lead to serialization.
100 for (size_t I = 0, E = Vars.size(); I < E; ++I) {
101 llvm::MCPhysReg Reg = Regs[I];
102 if (Vars[I].IsDef && IsUsedByAnyVar.count(Reg)) {
103 Vars[I].PossibleRegisters.remove(Reg);
104 }
105 }
106 // Create an MCInst and check assembly.
107 llvm::MCInst Inst = generateMCInst(InstrDesc, Vars, Regs);
108 if (!State.canAssemble(Inst))
109 continue;
110 Pattern.push_back(std::move(Inst));
111 }
112 return Pattern;
113 }
114
115 UopsBenchmarkRunner::~UopsBenchmarkRunner() = default;
116
117 const char *UopsBenchmarkRunner::getDisplayName() const { return "uops"; }
118
119 llvm::Expected> UopsBenchmarkRunner::createCode(
120 const LLVMState &State, const unsigned OpcodeIndex,
121 const unsigned NumRepetitions, const JitFunctionContext &Context) const {
122 const auto &InstrInfo = State.getInstrInfo();
123 const auto &RegInfo = State.getRegInfo();
124 const llvm::MCInstrDesc &InstrDesc = InstrInfo.get(OpcodeIndex);
125 for (const llvm::MCOperandInfo &OpInfo : InstrDesc.operands()) {
126 if (isInvalidOperand(OpInfo))
127 return makeError("Only registers and immediates are supported");
128 }
129
130 // FIXME: Load constants into registers (e.g. with fld1) to not break
131 // instructions like x87.
132
133 // Ideally we would like the only limitation on executing uops to be the issue
134 // ports. Maximizing port pressure increases the likelihood that the load is
135 // distributed evenly across possible ports.
136
137 // To achieve that, one approach is to generate instructions that do not have
138 // data dependencies between them.
139 //
140 // For some instructions, this is trivial:
141 // mov rax, qword ptr [rsi]
142 // mov rax, qword ptr [rsi]
143 // mov rax, qword ptr [rsi]
144 // mov rax, qword ptr [rsi]
145 // For the above snippet, haswell just renames rax four times and executes the
146 // four instructions two at a time on P23 and P0126.
147 //
148 // For some instructions, we just need to make sure that the source is
149 // different from the destination. For example, IDIV8r reads from GPR and
150 // writes to AX. We just need to ensure that the variable is assigned a
151 // register which is different from AX:
152 // idiv bx
153 // idiv bx
154 // idiv bx
155 // idiv bx
156 // The above snippet will be able to fully saturate the ports, while the same
157 // with ax would issue one uop every `latency(IDIV8r)` cycles.
158 //
159 // Some instructions make this harder because they both read and write from
160 // the same register:
161 // inc rax
162 // inc rax
163 // inc rax
164 // inc rax
165 // This has a data dependency from each instruction to the next, limit the
166 // number of instructions that can be issued in parallel.
167 // It turns out that this is not a big issue on recent Intel CPUs because they
168 // have heuristics to balance port pressure. In the snippet above, subsequent
169 // instructions will end up evenly distributed on {P0,P1,P5,P6}, but some CPUs
170 // might end up executing them all on P0 (just because they can), or try
171 // avoiding P5 because it's usually under high pressure from vector
172 // instructions.
173 // This issue is even more important for high-latency instructions because
174 // they increase the idle time of the CPU, e.g. :
175 // imul rax, rbx
176 // imul rax, rbx
177 // imul rax, rbx
178 // imul rax, rbx
179 //
180 // To avoid that, we do the renaming statically by generating as many
181 // independent exclusive assignments as possible (until all possible registers
182 // are exhausted) e.g.:
183 // imul rax, rbx
184 // imul rcx, rbx
185 // imul rdx, rbx
186 // imul r8, rbx
187 //
188 // Some instruction even make the above static renaming impossible because
189 // they implicitly read and write from the same operand, e.g. ADC16rr reads
190 // and writes from EFLAGS.
191 // In that case we just use a greedy register assignment and hope for the
192 // best.
193
194 const auto Vars = getVariables(RegInfo, InstrDesc, Context.getReservedRegs());
195
196 // Generate as many independent exclusive assignments as possible.
197 constexpr const int MaxStaticRenames = 20;
198 std::vector Pattern =
199 generateIndependentAssignments(State, InstrDesc, Vars, MaxStaticRenames);
200 if (Pattern.empty()) {
201 // We don't even have a single exclusive assignment, fallback to a greedy
202 // assignment.
203 // FIXME: Tell the user about this decision to help debugging.
204 const std::vector Regs = getGreedyAssignment(Vars);
205 if (!Vars.empty() && Regs.empty())
206 return makeError("No feasible greedy assignment");
207 llvm::MCInst Inst = generateMCInst(InstrDesc, Vars, Regs);
208 if (!State.canAssemble(Inst))
209 return makeError("Cannot assemble greedy assignment");
210 Pattern.push_back(std::move(Inst));
211 }
212
213 // Generate repetitions of the pattern until benchmark_iterations is reached.
214 std::vector Result;
215 Result.reserve(NumRepetitions);
216 for (unsigned I = 0; I < NumRepetitions; ++I)
217 Result.push_back(Pattern[I % Pattern.size()]);
218 return Result;
219 }
220
221 std::vector
222 UopsBenchmarkRunner::runMeasurements(const LLVMState &State,
223 const JitFunction &Function,
224 const unsigned NumRepetitions) const {
225 const auto &SchedModel = State.getSubtargetInfo().getSchedModel();
226
227 std::vector Result;
228 for (unsigned ProcResIdx = 1;
229 ProcResIdx < SchedModel.getNumProcResourceKinds(); ++ProcResIdx) {
230 const llvm::MCProcResourceDesc &ProcRes =
231 *SchedModel.getProcResource(ProcResIdx);
232 const std::string *const EventName =
233 getEventNameFromProcResName(ProcRes.Name);
234 if (!EventName)
235 continue;
236 pfm::Counter Counter{pfm::PerfEvent(*EventName)};
237 Counter.start();
238 Function();
239 Counter.stop();
240 Result.push_back({llvm::itostr(ProcResIdx),
241 static_cast(Counter.read()) / NumRepetitions,
242 ProcRes.Name});
243 }
244 return Result;
245 }
246
247 } // namespace exegesis
0 //===-- Uops.h --------------------------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// A BenchmarkRunner implementation to measure uop decomposition.
11 ///
12 //===----------------------------------------------------------------------===//
13
14 #ifndef LLVM_TOOLS_LLVM_EXEGESIS_UOPS_H
15 #define LLVM_TOOLS_LLVM_EXEGESIS_UOPS_H
16
17 #include "BenchmarkRunner.h"
18
19 namespace exegesis {
20
21 class UopsBenchmarkRunner : public BenchmarkRunner {
22 public:
23 ~UopsBenchmarkRunner() override;
24
25 private:
26 const char *getDisplayName() const override;
27
28 llvm::Expected>
29 createCode(const LLVMState &State, unsigned OpcodeIndex,
30 unsigned NumRepetitions,
31 const JitFunctionContext &Context) const override;
32
33 std::vector
34 runMeasurements(const LLVMState &State, const JitFunction &Function,
35 unsigned NumRepetitions) const override;
36 };
37
38 } // namespace exegesis
39
40 #endif // LLVM_TOOLS_LLVM_EXEGESIS_UOPS_H
0 //===-- X86.cpp --------------------------------------------------*- C++-*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "X86.h"
10
11 namespace exegesis {
12
13 static llvm::Error makeError(llvm::Twine Msg) {
14 return llvm::make_error(Msg,
15 llvm::inconvertibleErrorCode());
16 }
17
18 X86Filter::~X86Filter() = default;
19
20 // Test whether we can generate a snippet for this instruction.
21 llvm::Error X86Filter::shouldRun(const LLVMState &State,
22 const unsigned Opcode) const {
23 const auto &InstrInfo = State.getInstrInfo();
24 const llvm::MCInstrDesc &InstrDesc = InstrInfo.get(Opcode);
25 if (InstrDesc.isBranch() || InstrDesc.isIndirectBranch())
26 return makeError("Unsupported opcode: isBranch/isIndirectBranch");
27 if (InstrDesc.isCall() || InstrDesc.isReturn())
28 return makeError("Unsupported opcode: isCall/isReturn");
29 const auto OpcodeName = InstrInfo.getName(Opcode);
30 if (OpcodeName.startswith("POPF") || OpcodeName.startswith("PUSHF") ||
31 OpcodeName.startswith("ADJCALLSTACK")) {
32 return makeError("Unsupported opcode: Push/Pop/AdjCallStack");
33 }
34 return llvm::ErrorSuccess();
35 }
36
37 } // namespace exegesis
0 //===-- X86.h ---------------------------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// X86 target-specific setup.
11 ///
12 //===----------------------------------------------------------------------===//
13
14 #ifndef LLVM_TOOLS_LLVM_EXEGESIS_X86_H
15 #define LLVM_TOOLS_LLVM_EXEGESIS_X86_H
16
17 #include "BenchmarkRunner.h"
18 #include "LlvmState.h"
19
20 namespace exegesis {
21
22 class X86Filter : public BenchmarkRunner::InstructionFilter {
23 public:
24 ~X86Filter() override;
25
26 llvm::Error shouldRun(const LLVMState &State, unsigned Opcode) const override;
27 };
28
29 } // namespace exegesis
30
31 #endif // LLVM_TOOLS_LLVM_EXEGESIS_X86_H
0 //===-- llvm-exegesis.cpp ---------------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// Measures execution properties (latencies/uops) of an instruction.
11 ///
12 //===----------------------------------------------------------------------===//
13
14 #include "lib/BenchmarkResult.h"
15 #include "lib/BenchmarkRunner.h"
16 #include "lib/Latency.h"
17 #include "lib/LlvmState.h"
18 #include "lib/PerfHelper.h"
19 #include "lib/Uops.h"
20 #include "lib/X86.h"
21 #include "llvm/ADT/StringExtras.h"
22 #include "llvm/ADT/Twine.h"
23 #include "llvm/MC/MCInstBuilder.h"
24 #include "llvm/MC/MCRegisterInfo.h"
25 #include "llvm/Support/CommandLine.h"
26 #include "llvm/Support/Path.h"
27 #include "llvm/Support/TargetSelect.h"
28 #include
29 #include
30 #include
31 #include
32
33 static llvm::cl::opt
34 OpcodeIndex("opcode-index", llvm::cl::desc("opcode to measure, by index"),
35 llvm::cl::init(0));
36
37 static llvm::cl::opt
38 OpcodeName("opcode-name", llvm::cl::desc("opcode to measure, by name"),
39 llvm::cl::init(""));
40
41 enum class BenchmarkModeE { Latency, Uops };
42 static llvm::cl::opt
43 BenchmarkMode("benchmark-mode", llvm::cl::desc("the benchmark mode to run"),
44 llvm::cl::values(clEnumValN(BenchmarkModeE::Latency,
45 "latency", "Instruction Latency"),
46 clEnumValN(BenchmarkModeE::Uops, "uops",
47 "Uop Decomposition")));
48
49 static llvm::cl::opt
50 NumRepetitions("num-repetitions",
51 llvm::cl::desc("number of time to repeat the asm snippet"),
52 llvm::cl::init(10000));
53
54 namespace exegesis {
55
56 void main() {
57 if (OpcodeName.empty() == (OpcodeIndex == 0)) {
58 llvm::report_fatal_error(
59 "please provide one and only one of 'opcode-index' or 'opcode-name' ");
60 }
61
62 llvm::InitializeNativeTarget();
63 llvm::InitializeNativeTargetAsmPrinter();
64
65 // FIXME: Target-specific filter.
66 X86Filter Filter;
67
68 const LLVMState State;
69
70 unsigned Opcode = OpcodeIndex;
71 if (Opcode == 0) {
72 // Resolve opcode name -> opcode.
73 for (unsigned I = 0, E = State.getInstrInfo().getNumOpcodes(); I < E; ++I) {
74 if (State.getInstrInfo().getName(I) == OpcodeName) {
75 Opcode = I;
76 break;
77 }
78 }
79 if (Opcode == 0) {
80 llvm::report_fatal_error(
81 llvm::Twine("unknown opcode ").concat(OpcodeName));
82 }
83 }
84
85 std::unique_ptr Runner;
86 switch (BenchmarkMode) {
87 case BenchmarkModeE::Latency:
88 Runner = llvm::make_unique();
89 break;
90 case BenchmarkModeE::Uops:
91 Runner = llvm::make_unique();
92 break;
93 }
94
95 Runner->run(State, Opcode, NumRepetitions > 0 ? NumRepetitions : 1, Filter)
96 .writeYamlOrDie("-");
97 }
98
99 } // namespace exegesis
100
101 int main(int Argc, char **Argv) {
102 llvm::cl::ParseCommandLineOptions(Argc, Argv, "");
103
104 if (exegesis::pfm::pfmInitialize()) {
105 llvm::errs() << "cannot initialize libpfm\n";
106 return EXIT_FAILURE;
107 }
108
109 exegesis::main();
110
111 exegesis::pfm::pfmTerminate();
112 return EXIT_SUCCESS;
113 }
0 if(LLVM_TARGETS_TO_BUILD MATCHES "X86")
1 add_subdirectory(llvm-cfi-verify)
1 add_subdirectory(
2 llvm-cfi-verify
3 )
4 add_subdirectory(
5 llvm-exegesis
6 )
27 endif()
38
0 //===-- BenchmarkResultTest.cpp ---------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "BenchmarkResult.h"
10 #include "llvm/ADT/SmallString.h"
11 #include "llvm/Support/Error.h"
12 #include "llvm/Support/YAMLTraits.h"
13 #include "llvm/Support/raw_ostream.h"
14 #include "gmock/gmock.h"
15 #include "gtest/gtest.h"
16
17 namespace exegesis {
18
19 bool operator==(const BenchmarkMeasure &A, const BenchmarkMeasure &B) {
20 return std::tie(A.Key, A.Value) == std::tie(B.Key, B.Value);
21 }
22
23 namespace {
24
25 TEST(BenchmarkResultTest, WriteToAndReadFromDisk) {
26 InstructionBenchmark ToDisk;
27
28 ToDisk.AsmTmpl.Name = "name";
29 ToDisk.CpuName = "cpu_name";
30 ToDisk.LLVMTriple = "llvm_triple";
31 ToDisk.NumRepetitions = 1;
32 ToDisk.Measurements.push_back(BenchmarkMeasure{"a", 1, "debug a"});
33 ToDisk.Measurements.push_back(BenchmarkMeasure{"b", 2});
34 ToDisk.Error = "error";
35
36 const llvm::StringRef Filename("data.yaml");
37
38 ToDisk.writeYamlOrDie(Filename);
39
40 {
41 const auto FromDisk = InstructionBenchmark::readYamlOrDie(Filename);
42
43 EXPECT_EQ(FromDisk.AsmTmpl.Name, ToDisk.AsmTmpl.Name);
44 EXPECT_EQ(FromDisk.CpuName, ToDisk.CpuName);
45 EXPECT_EQ(FromDisk.LLVMTriple, ToDisk.LLVMTriple);
46 EXPECT_EQ(FromDisk.NumRepetitions, ToDisk.NumRepetitions);
47 EXPECT_THAT(FromDisk.Measurements, ToDisk.Measurements);
48 EXPECT_THAT(FromDisk.Error, ToDisk.Error);
49 }
50 }
51
52 } // namespace
53 } // namespace exegesis
0 include_directories(
1 ${CMAKE_SOURCE_DIR}/lib/Target/X86
2 ${CMAKE_BINARY_DIR}/lib/Target/X86
3 ${CMAKE_SOURCE_DIR}/tools/llvm-exegesis/lib
4 )
5
6 set(LLVM_LINK_COMPONENTS
7 MC
8 MCParser
9 Object
10 Support
11 Symbolize
12 native
13 )
14
15 add_llvm_unittest(LLVMExegesisTests
16 BenchmarkResultTest.cpp
17 InMemoryAssemblerTest.cpp
18 InstructionSnippetGeneratorTest.cpp
19 OperandGraphTest.cpp
20 PerfHelperTest.cpp
21 )
22 target_link_libraries(LLVMExegesisTests PRIVATE LLVMExegesis)
23
24 if(HAVE_LIBPFM)
25 target_link_libraries(LLVMExegesisTests PRIVATE pfm)
26 endif()
27
0 //===-- InMemoryAssemblerTest.cpp -------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "InMemoryAssembler.h"
10 #include "X86InstrInfo.h"
11 #include "llvm/ADT/ArrayRef.h"
12 #include "llvm/CodeGen/MachineInstrBuilder.h"
13 #include "llvm/CodeGen/TargetInstrInfo.h"
14 #include "llvm/CodeGen/TargetSubtargetInfo.h"
15 #include "llvm/MC/MCInstBuilder.h"
16 #include "llvm/Support/Host.h"
17 #include "llvm/Support/TargetRegistry.h"
18 #include "llvm/Support/TargetSelect.h"
19 #include "gmock/gmock.h"
20 #include "gtest/gtest.h"
21 #include
22
23 namespace exegesis {
24 namespace {
25
26 using llvm::MCInstBuilder;
27 using llvm::X86::EAX;
28 using llvm::X86::MOV32ri;
29 using llvm::X86::MOV64ri32;
30 using llvm::X86::RAX;
31 using llvm::X86::XOR32rr;
32 using testing::ElementsAre;
33
34 class MachineFunctionGeneratorTest : public ::testing::Test {
35 protected:
36 MachineFunctionGeneratorTest()
37 : TT(llvm::sys::getProcessTriple()),
38 CpuName(llvm::sys::getHostCPUName().str()) {}
39
40 static void SetUpTestCase() {
41 llvm::InitializeNativeTarget();
42 llvm::InitializeNativeTargetAsmPrinter();
43 }
44
45 std::unique_ptr createTargetMachine() {
46 std::string Error;
47 const llvm::Target *TheTarget =
48 llvm::TargetRegistry::lookupTarget(TT, Error);
49 assert(TheTarget);
50 const llvm::TargetOptions Options;
51 return std::unique_ptr(
52 static_cast(TheTarget->createTargetMachine(
53 TT, CpuName, "", Options, llvm::Reloc::Model::Static)));
54 }
55
56 private:
57 const std::string TT;
58 const std::string CpuName;
59 };
60
61 TEST_F(MachineFunctionGeneratorTest, JitFunction) {
62 JitFunctionContext Context(createTargetMachine());
63 JitFunction Function(std::move(Context), {});
64 ASSERT_THAT(Function.getFunctionBytes().str(), ElementsAre(0xc3));
65 Function();
66 }
67
68 TEST_F(MachineFunctionGeneratorTest, JitFunctionXOR32rr) {
69 JitFunctionContext Context(createTargetMachine());
70 JitFunction Function(
71 std::move(Context),
72 {MCInstBuilder(XOR32rr).addReg(EAX).addReg(EAX).addReg(EAX)});
73 ASSERT_THAT(Function.getFunctionBytes().str(), ElementsAre(0x31, 0xc0, 0xc3));
74 Function();
75 }
76
77 TEST_F(MachineFunctionGeneratorTest, JitFunctionMOV64ri) {
78 JitFunctionContext Context(createTargetMachine());
79 JitFunction Function(std::move(Context),
80 {MCInstBuilder(MOV64ri32).addReg(RAX).addImm(42)});
81 ASSERT_THAT(Function.getFunctionBytes().str(),
82 ElementsAre(0x48, 0xc7, 0xc0, 0x2a, 0x00, 0x00, 0x00, 0xc3));
83 Function();
84 }
85
86 TEST_F(MachineFunctionGeneratorTest, JitFunctionMOV32ri) {
87 JitFunctionContext Context(createTargetMachine());
88 JitFunction Function(std::move(Context),
89 {MCInstBuilder(MOV32ri).addReg(EAX).addImm(42)});
90 ASSERT_THAT(Function.getFunctionBytes().str(),
91 ElementsAre(0xb8, 0x2a, 0x00, 0x00, 0x00, 0xc3));
92 Function();
93 }
94
95 } // namespace
96 } // namespace exegesis
0 //===-- InstructionSnippetGeneratorTest.cpp ---------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "InstructionSnippetGenerator.h"
10 #include "X86InstrInfo.h"
11 #include "llvm/MC/MCInstBuilder.h"
12 #include "llvm/Support/Host.h"
13 #include "llvm/Support/TargetRegistry.h"
14 #include "llvm/Support/TargetSelect.h"
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
17 #include
18 #include
19
20 namespace llvm {
21
22 bool operator==(const MCOperand &A, const MCOperand &B) {
23 if ((A.isValid() == false) && (B.isValid() == false))
24 return true;
25 if (A.isReg() && B.isReg())
26 return A.getReg() == B.getReg();
27 if (A.isImm() && B.isImm())
28 return A.getImm() == B.getImm();
29 return false;
30 }
31
32 } // namespace llvm
33
34 namespace exegesis {
35 namespace {
36
37 using testing::_;
38 using testing::AllOf;
39 using testing::AnyOf;
40 using testing::Contains;
41 using testing::ElementsAre;
42 using testing::Eq;
43 using testing::Field;
44 using testing::Not;
45 using testing::SizeIs;
46 using testing::UnorderedElementsAre;
47 using testing::Value;
48
49 using llvm::X86::AL;
50 using llvm::X86::AX;
51 using llvm::X86::EFLAGS;
52 using llvm::X86::RAX;
53
54 class MCInstrDescViewTest : public ::testing::Test {
55 protected:
56 MCInstrDescViewTest()
57 : TheTriple(llvm::sys::getProcessTriple()),
58 CpuName(llvm::sys::getHostCPUName().str()) {}
59
60 void SetUp() override {
61 llvm::InitializeNativeTarget();
62
63 std::string Error;
64 const auto *Target = llvm::TargetRegistry::lookupTarget(TheTriple, Error);
65 InstrInfo.reset(Target->createMCInstrInfo());
66 RegInfo.reset(Target->createMCRegInfo(TheTriple));
67 }
68
69 const std::string TheTriple;
70 const std::string CpuName;
71 std::unique_ptr InstrInfo;
72 std::unique_ptr RegInfo;
73 };
74
75 MATCHER(IsDef, "") { return arg.IsDef; }
76 MATCHER(IsUse, "") { return arg.IsUse; }
77 MATCHER_P2(EqVarAssignement, VariableIndexMatcher, AssignedRegisterMatcher,
78 "") {
79 return Value(
80 arg,
81 AllOf(Field(&VariableAssignment::VarIdx, VariableIndexMatcher),
82 Field(&VariableAssignment::AssignedReg, AssignedRegisterMatcher)));
83 }
84
85 size_t returnIndexZero(const size_t UpperBound) { return 0; }
86
87 TEST_F(MCInstrDescViewTest, XOR64rr) {
88 const llvm::MCInstrDesc &InstrDesc = InstrInfo->get(llvm::X86::XOR64rr);
89 const auto Vars =
90 getVariables(*RegInfo, InstrDesc, llvm::BitVector(RegInfo->getNumRegs()));
91
92 // XOR64rr has the following operands:
93 // 0. out register
94 // 1. in register (tied to out)
95 // 2. in register
96 // 3. out EFLAGS (implicit)
97 //
98 // This translates to 3 variables, one for 0 and 1, one for 2, one for 3.
99 ASSERT_THAT(Vars, SizeIs(3));
100
101 EXPECT_THAT(Vars[0].ExplicitOperands, ElementsAre(0, 1));
102 EXPECT_THAT(Vars[1].ExplicitOperands, ElementsAre(2));
103 EXPECT_THAT(Vars[2].ExplicitOperands, ElementsAre()); // implicit
104
105 EXPECT_THAT(Vars[0], AllOf(IsUse(), IsDef()));
106 EXPECT_THAT(Vars[1], AllOf(IsUse(), Not(IsDef())));
107 EXPECT_THAT(Vars[2], AllOf(Not(IsUse()), IsDef()));
108
109 EXPECT_THAT(Vars[0].PossibleRegisters, Contains(RAX));
110 EXPECT_THAT(Vars[1].PossibleRegisters, Contains(RAX));
111 EXPECT_THAT(Vars[2].PossibleRegisters, ElementsAre(EFLAGS));
112
113 // Computing chains.
114 const auto Chains = computeSequentialAssignmentChains(*RegInfo, Vars);
115
116 // Because operands 0 and 1 are tied together any possible value for variable
117 // 0 would do.
118 for (const auto &Reg : Vars[0].PossibleRegisters) {
119 EXPECT_THAT(Chains, Contains(ElementsAre(EqVarAssignement(0, Reg))));
120 }
121
122 // We also have chains going through operand 0 to 2 (i.e. Vars 0 and 1).
123 EXPECT_THAT(Vars[0].PossibleRegisters, Eq(Vars[1].PossibleRegisters))
124 << "Variables 0 and 1 are of the same class";
125 for (const auto &Reg : Vars[0].PossibleRegisters) {
126 EXPECT_THAT(Chains,
127 Contains(UnorderedElementsAre(EqVarAssignement(0, Reg),
128 EqVarAssignement(1, Reg))));
129 }
130
131 // EFLAGS does not appear as an input therefore no chain can contain EFLAGS.
132 EXPECT_THAT(Chains, Not(Contains(Contains(EqVarAssignement(_, EFLAGS)))));
133
134 // Computing assignment.
135 const auto Regs = getRandomAssignment(Vars, Chains, &returnIndexZero);
136 EXPECT_THAT(Regs, ElementsAre(RAX, RAX, EFLAGS));
137
138 // Generating assembler representation.
139 const llvm::MCInst Inst = generateMCInst(InstrDesc, Vars, Regs);
140 EXPECT_THAT(Inst.getOpcode(), llvm::X86::XOR64rr);
141 EXPECT_THAT(Inst.getNumOperands(), 3);
142 EXPECT_THAT(Inst.getOperand(0), llvm::MCOperand::createReg(RAX));
143 EXPECT_THAT(Inst.getOperand(1), llvm::MCOperand::createReg(RAX));
144 EXPECT_THAT(Inst.getOperand(2), llvm::MCOperand::createReg(RAX));
145 }
146
147 TEST_F(MCInstrDescViewTest, AAA) {
148 const llvm::MCInstrDesc &InstrDesc = InstrInfo->get(llvm::X86::AAA);
149 const auto Vars =
150 getVariables(*RegInfo, InstrDesc, llvm::BitVector(RegInfo->getNumRegs()));
151
152 // AAA has the following operands:
153 // 0. out AX (implicit)
154 // 1. out EFLAGS (implicit)
155 // 2. in AL (implicit)
156 // 3. in EFLAGS (implicit)
157 //
158 // This translates to 4 Vars (non are tied together).
159 ASSERT_THAT(Vars, SizeIs(4));
160
161 EXPECT_THAT(Vars[0].ExplicitOperands, ElementsAre()); // implicit
162 EXPECT_THAT(Vars[1].ExplicitOperands, ElementsAre()); // implicit
163 EXPECT_THAT(Vars[2].ExplicitOperands, ElementsAre()); // implicit
164 EXPECT_THAT(Vars[3].ExplicitOperands, ElementsAre()); // implicit
165
166 EXPECT_THAT(Vars[0], AllOf(Not(IsUse()), IsDef()));
167 EXPECT_THAT(Vars[1], AllOf(Not(IsUse()), IsDef()));
168 EXPECT_THAT(Vars[2], AllOf(IsUse(), Not(IsDef())));
169 EXPECT_THAT(Vars[3], AllOf(IsUse(), Not(IsDef())));
170
171 EXPECT_THAT(Vars[0].PossibleRegisters, ElementsAre(AX));
172 EXPECT_THAT(Vars[1].PossibleRegisters, ElementsAre(EFLAGS));
173 EXPECT_THAT(Vars[2].PossibleRegisters, ElementsAre(AL));
174 EXPECT_THAT(Vars[3].PossibleRegisters, ElementsAre(EFLAGS));
175
176 const auto Chains = computeSequentialAssignmentChains(*RegInfo, Vars);
177 EXPECT_THAT(Chains,
178 ElementsAre(UnorderedElementsAre(EqVarAssignement(0, AX),
179 EqVarAssignement(2, AL)),
180 UnorderedElementsAre(EqVarAssignement(1, EFLAGS),
181 EqVarAssignement(3, EFLAGS))));
182
183 // Computing assignment.
184 const auto Regs = getRandomAssignment(Vars, Chains, &returnIndexZero);
185 EXPECT_THAT(Regs, ElementsAre(AX, EFLAGS, AL, EFLAGS));
186
187 // Generating assembler representation.
188 const llvm::MCInst Inst = generateMCInst(InstrDesc, Vars, Regs);
189 EXPECT_THAT(Inst.getOpcode(), llvm::X86::AAA);
190 EXPECT_THAT(Inst.getNumOperands(), 0) << "All operands are implicit";
191 }
192
193 TEST_F(MCInstrDescViewTest, ReservedRegisters) {
194 llvm::BitVector ReservedRegisters(RegInfo->getNumRegs());
195
196 const llvm::MCInstrDesc &InstrDesc = InstrInfo->get(llvm::X86::XOR64rr);
197 {
198 const auto Vars = getVariables(*RegInfo, InstrDesc, ReservedRegisters);
199 ASSERT_THAT(Vars, SizeIs(3));
200 EXPECT_THAT(Vars[0].PossibleRegisters, Contains(RAX));
201 EXPECT_THAT(Vars[1].PossibleRegisters, Contains(RAX));
202 }
203
204 // Disable RAX.
205 ReservedRegisters.set(RAX);
206 {
207 const auto Vars = getVariables(*RegInfo, InstrDesc, ReservedRegisters);
208 ASSERT_THAT(Vars, SizeIs(3));
209 EXPECT_THAT(Vars[0].PossibleRegisters, Not(Contains(RAX)));
210 EXPECT_THAT(Vars[1].PossibleRegisters, Not(Contains(RAX)));
211 }
212 }
213
214 Variable makeVariableWithRegisters(bool IsReg,
215 std::initializer_list Regs) {
216 assert((IsReg || (Regs.size() == 0)) && "IsReg => !(Regs.size() == 0)");
217 Variable Var;
218 Var.IsReg = IsReg;
219 Var.PossibleRegisters.insert(Regs.begin(), Regs.end());
220 return Var;
221 }
222
223 TEST(getExclusiveAssignment, TriviallyFeasible) {
224 const std::vector Vars = {
225 makeVariableWithRegisters(true, {3}),
226 makeVariableWithRegisters(false, {}),
227 makeVariableWithRegisters(true, {4}),
228 makeVariableWithRegisters(true, {5}),
229 };
230 const auto Regs = getExclusiveAssignment(Vars);
231 EXPECT_THAT(Regs, ElementsAre(3, 0, 4, 5));
232 }
233
234 TEST(getExclusiveAssignment, TriviallyInfeasible1) {
235 const std::vector Vars = {
236 makeVariableWithRegisters(true, {3}),
237 makeVariableWithRegisters(true, {}),
238 makeVariableWithRegisters(true, {4}),
239 makeVariableWithRegisters(true, {5}),
240 };
241 const auto Regs = getExclusiveAssignment(Vars);
242 EXPECT_THAT(Regs, ElementsAre());
243 }
244
245 TEST(getExclusiveAssignment, TriviallyInfeasible) {
246 const std::vector Vars = {
247 makeVariableWithRegisters(true, {4}),
248 makeVariableWithRegisters(true, {4}),
249 };
250 const auto Regs = getExclusiveAssignment(Vars);
251 EXPECT_THAT(Regs, ElementsAre());
252 }
253
254 TEST(getExclusiveAssignment, Feasible1) {
255 const std::vector Vars = {
256 makeVariableWithRegisters(true, {4, 3}),
257 makeVariableWithRegisters(true, {6, 3}),
258 makeVariableWithRegisters(true, {6, 4}),
259 };
260 const auto Regs = getExclusiveAssignment(Vars);
261 ASSERT_THAT(Regs, AnyOf(ElementsAre(3, 6, 4), ElementsAre(4, 3, 6)));
262 }
263
264 TEST(getExclusiveAssignment, Feasible2) {
265 const std::vector Vars = {
266 makeVariableWithRegisters(true, {1, 2}),
267 makeVariableWithRegisters(true, {3, 4}),
268 };
269 const auto Regs = getExclusiveAssignment(Vars);
270 ASSERT_THAT(Regs, AnyOf(ElementsAre(1, 3), ElementsAre(1, 4),
271 ElementsAre(2, 3), ElementsAre(2, 4)));
272 }
273
274 TEST(getGreedyAssignment, Infeasible) {
275 const std::vector Vars = {
276 makeVariableWithRegisters(true, {}),
277 makeVariableWithRegisters(true, {1, 2}),
278 };
279 const auto Regs = getGreedyAssignment(Vars);
280 ASSERT_THAT(Regs, ElementsAre());
281 }
282
283 TEST(getGreedyAssignment, FeasibleNoFallback) {
284 const std::vector Vars = {
285 makeVariableWithRegisters(true, {1, 2}),
286 makeVariableWithRegisters(false, {}),
287 makeVariableWithRegisters(true, {2, 3}),
288 };
289 const auto Regs = getGreedyAssignment(Vars);
290 ASSERT_THAT(Regs, ElementsAre(1, 0, 2));
291 }
292
293 TEST(getGreedyAssignment, Feasible) {
294 const std::vector Vars = {
295 makeVariableWithRegisters(false, {}),
296 makeVariableWithRegisters(true, {1, 2}),
297 makeVariableWithRegisters(true, {2, 3}),
298 makeVariableWithRegisters(true, {2, 3}),
299 makeVariableWithRegisters(true, {2, 3}),
300 };
301 const auto Regs = getGreedyAssignment(Vars);
302 ASSERT_THAT(Regs, ElementsAre(0, 1, 2, 3, 2));
303 }
304
305 } // namespace
306 } // namespace exegesis
0 //===-- OperandGraphTest.cpp ------------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "OperandGraph.h"
10 #include "gmock/gmock.h"
11 #include "gtest/gtest.h"
12
13 using testing::ElementsAre;
14 using testing::IsEmpty;
15 using testing::Not;
16
17 namespace exegesis {
18 namespace graph {
19 namespace {
20
21 static const auto In = Node::In();
22 static const auto Out = Node::Out();
23
24 TEST(OperandGraphTest, NoPath) {
25 Graph TheGraph;
26 EXPECT_THAT(TheGraph.getPathFrom(In, Out), IsEmpty());
27 }
28
29 TEST(OperandGraphTest, Connecting) {
30 Graph TheGraph;
31 TheGraph.connect(In, Out);
32 EXPECT_THAT(TheGraph.getPathFrom(In, Out), Not(IsEmpty()));
33 EXPECT_THAT(TheGraph.getPathFrom(In, Out), ElementsAre(In, Out));
34 }
35
36 TEST(OperandGraphTest, ConnectingThroughVariable) {
37 const Node Var = Node::Var(1);
38 Graph TheGraph;
39 TheGraph.connect(In, Var);
40 TheGraph.connect(Var, Out);
41 EXPECT_THAT(TheGraph.getPathFrom(In, Out), Not(IsEmpty()));
42 EXPECT_THAT(TheGraph.getPathFrom(In, Out), ElementsAre(In, Var, Out));
43 }
44
45 } // namespace
46 } // namespace graph
47 } // namespace exegesis
0 //===-- PerfHelperTest.cpp --------------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "PerfHelper.h"
10 #include "llvm/Config/config.h"
11 #include "gmock/gmock.h"
12 #include "gtest/gtest.h"
13
14 namespace exegesis {
15 namespace pfm {
16 namespace {
17
18 using ::testing::IsEmpty;
19 using ::testing::Not;
20
21 TEST(PerfHelperTest, FunctionalTest) {
22 #ifdef HAVE_LIBPFM
23 ASSERT_FALSE(pfmInitialize());
24 const PerfEvent SingleEvent("CYCLES:u");
25 const auto &EmptyFn = []() {};
26 std::string CallbackEventName;
27 std::string CallbackEventNameFullyQualifed;
28 int64_t CallbackEventCycles;
29 Measure(llvm::makeArrayRef(SingleEvent),
30 [&](const PerfEvent &Event, int64_t Value) {
31 CallbackEventName = Event.name();
32 CallbackEventNameFullyQualifed = Event.getPfmEventString();
33 CallbackEventCycles = Value;
34 },
35 EmptyFn);
36 EXPECT_EQ(CallbackEventName, "CYCLES:u");
37 EXPECT_THAT(CallbackEventNameFullyQualifed, Not(IsEmpty()));
38 pfmTerminate();
39 #else
40 ASSERT_TRUE(PfmInitialize());
41 #endif
42 }
43
44 } // namespace
45 } // namespace pfm
46 } // namespace exegesis