1 //===-- BenchmarkRunner.cpp -------------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
12 #include "Assembler.h"
13 #include "BenchmarkRunner.h"
15 #include "MCInstrDescView.h"
16 #include "PerfHelper.h"
17 #include "llvm/ADT/StringExtras.h"
18 #include "llvm/ADT/StringRef.h"
19 #include "llvm/ADT/Twine.h"
20 #include "llvm/Support/CrashRecoveryContext.h"
21 #include "llvm/Support/FileSystem.h"
22 #include "llvm/Support/MemoryBuffer.h"
23 #include "llvm/Support/Program.h"
28 BenchmarkRunner::BenchmarkRunner(const LLVMState
&State
,
29 InstructionBenchmark::ModeE Mode
)
30 : State(State
), Mode(Mode
), Scratch(std::make_unique
<ScratchSpace
>()) {}
32 BenchmarkRunner::~BenchmarkRunner() = default;
35 class FunctionExecutorImpl
: public BenchmarkRunner::FunctionExecutor
{
37 FunctionExecutorImpl(const LLVMState
&State
,
38 object::OwningBinary
<object::ObjectFile
> Obj
,
39 BenchmarkRunner::ScratchSpace
*Scratch
)
40 : Function(State
.createTargetMachine(), std::move(Obj
)),
44 Expected
<int64_t> runAndMeasure(const char *Counters
) const override
{
45 // We sum counts when there are several counters for a single ProcRes
46 // (e.g. P23 on SandyBridge).
47 int64_t CounterValue
= 0;
48 SmallVector
<StringRef
, 2> CounterNames
;
49 StringRef(Counters
).split(CounterNames
, '+');
50 char *const ScratchPtr
= Scratch
->ptr();
51 for (auto &CounterName
: CounterNames
) {
52 CounterName
= CounterName
.trim();
53 pfm::PerfEvent
PerfEvent(CounterName
);
54 if (!PerfEvent
.valid())
56 Twine("invalid perf event '").concat(CounterName
).concat("'"));
57 pfm::Counter
Counter(PerfEvent
);
60 CrashRecoveryContext CRC
;
61 CrashRecoveryContext::Enable();
62 const bool Crashed
= !CRC
.RunSafely([this, &Counter
, ScratchPtr
]() {
64 this->Function(ScratchPtr
);
67 CrashRecoveryContext::Disable();
68 // FIXME: Better diagnosis.
70 return make_error
<Failure
>("snippet crashed while running");
72 CounterValue
+= Counter
.read();
77 const ExecutableFunction Function
;
78 BenchmarkRunner::ScratchSpace
*const Scratch
;
82 InstructionBenchmark
BenchmarkRunner::runConfiguration(
83 const BenchmarkCode
&BC
, unsigned NumRepetitions
,
84 const SnippetRepetitor
&Repetitor
, bool DumpObjectToDisk
) const {
85 InstructionBenchmark InstrBenchmark
;
86 InstrBenchmark
.Mode
= Mode
;
87 InstrBenchmark
.CpuName
= State
.getTargetMachine().getTargetCPU();
88 InstrBenchmark
.LLVMTriple
=
89 State
.getTargetMachine().getTargetTriple().normalize();
90 InstrBenchmark
.NumRepetitions
= NumRepetitions
;
91 InstrBenchmark
.Info
= BC
.Info
;
93 const std::vector
<MCInst
> &Instructions
= BC
.Key
.Instructions
;
95 InstrBenchmark
.Key
= BC
.Key
;
97 // Assemble at least kMinInstructionsForSnippet instructions by repeating the
98 // snippet for debug/analysis. This is so that the user clearly understands
99 // that the inside instructions are repeated.
100 constexpr const int kMinInstructionsForSnippet
= 16;
102 SmallString
<0> Buffer
;
103 raw_svector_ostream
OS(Buffer
);
104 assembleToStream(State
.getExegesisTarget(), State
.createTargetMachine(),
105 BC
.LiveIns
, BC
.Key
.RegisterInitialValues
,
106 Repetitor
.Repeat(Instructions
, kMinInstructionsForSnippet
),
108 const ExecutableFunction
EF(State
.createTargetMachine(),
109 getObjectFromBuffer(OS
.str()));
110 const auto FnBytes
= EF
.getFunctionBytes();
111 InstrBenchmark
.AssembledSnippet
.assign(FnBytes
.begin(), FnBytes
.end());
114 // Assemble NumRepetitions instructions repetitions of the snippet for
117 Repetitor
.Repeat(Instructions
, InstrBenchmark
.NumRepetitions
);
119 object::OwningBinary
<object::ObjectFile
> ObjectFile
;
120 if (DumpObjectToDisk
) {
121 auto ObjectFilePath
= writeObjectFile(BC
, Filler
);
122 if (Error E
= ObjectFilePath
.takeError()) {
123 InstrBenchmark
.Error
= toString(std::move(E
));
124 return InstrBenchmark
;
126 outs() << "Check generated assembly with: /usr/bin/objdump -d "
127 << *ObjectFilePath
<< "\n";
128 ObjectFile
= getObjectFromFile(*ObjectFilePath
);
130 SmallString
<0> Buffer
;
131 raw_svector_ostream
OS(Buffer
);
132 assembleToStream(State
.getExegesisTarget(), State
.createTargetMachine(),
133 BC
.LiveIns
, BC
.Key
.RegisterInitialValues
, Filler
, OS
);
134 ObjectFile
= getObjectFromBuffer(OS
.str());
137 const FunctionExecutorImpl
Executor(State
, std::move(ObjectFile
),
139 auto Measurements
= runMeasurements(Executor
);
140 if (Error E
= Measurements
.takeError()) {
141 InstrBenchmark
.Error
= toString(std::move(E
));
142 return InstrBenchmark
;
144 InstrBenchmark
.Measurements
= std::move(*Measurements
);
145 assert(InstrBenchmark
.NumRepetitions
> 0 && "invalid NumRepetitions");
146 for (BenchmarkMeasure
&BM
: InstrBenchmark
.Measurements
) {
147 // Scale the measurements by instruction.
148 BM
.PerInstructionValue
/= InstrBenchmark
.NumRepetitions
;
149 // Scale the measurements by snippet.
150 BM
.PerSnippetValue
*= static_cast<double>(Instructions
.size()) /
151 InstrBenchmark
.NumRepetitions
;
154 return InstrBenchmark
;
157 Expected
<std::string
>
158 BenchmarkRunner::writeObjectFile(const BenchmarkCode
&BC
,
159 const FillFunction
&FillFunction
) const {
161 SmallString
<256> ResultPath
;
162 if (Error E
= errorCodeToError(
163 sys::fs::createTemporaryFile("snippet", "o", ResultFD
, ResultPath
)))
165 raw_fd_ostream
OFS(ResultFD
, true /*ShouldClose*/);
166 assembleToStream(State
.getExegesisTarget(), State
.createTargetMachine(),
167 BC
.LiveIns
, BC
.Key
.RegisterInitialValues
, FillFunction
, OFS
);
168 return ResultPath
.str();
171 BenchmarkRunner::FunctionExecutor::~FunctionExecutor() {}
173 } // namespace exegesis