1 //===-- BenchmarkResult.cpp -------------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "BenchmarkResult.h"
11 #include "llvm/ADT/STLExtras.h"
12 #include "llvm/ADT/StringRef.h"
13 #include "llvm/ObjectYAML/YAML.h"
14 #include "llvm/Support/FileOutputBuffer.h"
15 #include "llvm/Support/FileSystem.h"
16 #include "llvm/Support/Format.h"
17 #include "llvm/Support/raw_ostream.h"
19 static constexpr const char kIntegerFormat
[] = "i_0x%" PRId64
"x";
20 static constexpr const char kDoubleFormat
[] = "f_%la";
22 static void serialize(const exegesis::BenchmarkResultContext
&Context
,
23 const llvm::MCOperand
&MCOperand
, llvm::raw_ostream
&OS
) {
24 if (MCOperand
.isReg()) {
25 OS
<< Context
.getRegName(MCOperand
.getReg());
26 } else if (MCOperand
.isImm()) {
27 OS
<< llvm::format(kIntegerFormat
, MCOperand
.getImm());
28 } else if (MCOperand
.isFPImm()) {
29 OS
<< llvm::format(kDoubleFormat
, MCOperand
.getFPImm());
35 static void serialize(const exegesis::BenchmarkResultContext
&Context
,
36 const llvm::MCInst
&MCInst
, llvm::raw_ostream
&OS
) {
37 OS
<< Context
.getInstrName(MCInst
.getOpcode());
38 for (const auto &Op
: MCInst
) {
40 serialize(Context
, Op
, OS
);
44 static llvm::MCOperand
45 deserialize(const exegesis::BenchmarkResultContext
&Context
,
46 llvm::StringRef String
) {
47 assert(!String
.empty());
49 double DoubleValue
= 0;
50 if (sscanf(String
.data(), kIntegerFormat
, &IntValue
) == 1)
51 return llvm::MCOperand::createImm(IntValue
);
52 if (sscanf(String
.data(), kDoubleFormat
, &DoubleValue
) == 1)
53 return llvm::MCOperand::createFPImm(DoubleValue
);
54 if (unsigned RegNo
= Context
.getRegNo(String
)) // Returns 0 if invalid.
55 return llvm::MCOperand::createReg(RegNo
);
59 static llvm::StringRef
60 deserialize(const exegesis::BenchmarkResultContext
&Context
,
61 llvm::StringRef String
, llvm::MCInst
&Value
) {
62 llvm::SmallVector
<llvm::StringRef
, 8> Pieces
;
63 String
.split(Pieces
, " ", /* MaxSplit */ -1, /* KeepEmpty */ false);
65 return "Invalid Instruction";
66 bool ProcessOpcode
= true;
67 for (llvm::StringRef Piece
: Pieces
) {
69 ProcessOpcode
= false;
70 Value
.setOpcode(Context
.getInstrOpcode(Piece
));
71 if (Value
.getOpcode() == 0)
72 return "Unknown Opcode Name";
74 Value
.addOperand(deserialize(Context
, Piece
));
80 // YAML IO requires a mutable pointer to Context but we guarantee to not
82 static void *getUntypedContext(const exegesis::BenchmarkResultContext
&Ctx
) {
83 return const_cast<exegesis::BenchmarkResultContext
*>(&Ctx
);
86 static const exegesis::BenchmarkResultContext
&getTypedContext(void *Ctx
) {
88 return *static_cast<const exegesis::BenchmarkResultContext
*>(Ctx
);
91 // Defining YAML traits for IO.
95 // std::vector<llvm::MCInst> will be rendered as a list.
96 template <> struct SequenceElementTraits
<llvm::MCInst
> {
97 static const bool flow
= false;
100 template <> struct ScalarTraits
<llvm::MCInst
> {
102 static void output(const llvm::MCInst
&Value
, void *Ctx
,
103 llvm::raw_ostream
&Out
) {
104 serialize(getTypedContext(Ctx
), Value
, Out
);
107 static StringRef
input(StringRef Scalar
, void *Ctx
, llvm::MCInst
&Value
) {
108 return deserialize(getTypedContext(Ctx
), Scalar
, Value
);
111 static QuotingType
mustQuote(StringRef
) { return QuotingType::Single
; }
113 static const bool flow
= true;
116 // std::vector<exegesis::Measure> will be rendered as a list.
117 template <> struct SequenceElementTraits
<exegesis::BenchmarkMeasure
> {
118 static const bool flow
= false;
121 // exegesis::Measure is rendererd as a flow instead of a list.
122 // e.g. { "key": "the key", "value": 0123 }
123 template <> struct MappingTraits
<exegesis::BenchmarkMeasure
> {
124 static void mapping(IO
&Io
, exegesis::BenchmarkMeasure
&Obj
) {
125 Io
.mapRequired("key", Obj
.Key
);
126 Io
.mapRequired("value", Obj
.Value
);
127 Io
.mapOptional("debug_string", Obj
.DebugString
);
129 static const bool flow
= true;
133 struct ScalarEnumerationTraits
<exegesis::InstructionBenchmark::ModeE
> {
134 static void enumeration(IO
&Io
,
135 exegesis::InstructionBenchmark::ModeE
&Value
) {
136 Io
.enumCase(Value
, "", exegesis::InstructionBenchmark::Unknown
);
137 Io
.enumCase(Value
, "latency", exegesis::InstructionBenchmark::Latency
);
138 Io
.enumCase(Value
, "uops", exegesis::InstructionBenchmark::Uops
);
142 template <> struct MappingTraits
<exegesis::InstructionBenchmarkKey
> {
143 static void mapping(IO
&Io
, exegesis::InstructionBenchmarkKey
&Obj
) {
144 Io
.mapRequired("instructions", Obj
.Instructions
);
145 Io
.mapOptional("config", Obj
.Config
);
149 template <> struct MappingTraits
<exegesis::InstructionBenchmark
> {
150 class NormalizedBinary
{
152 NormalizedBinary(IO
&io
) {}
153 NormalizedBinary(IO
&, std::vector
<uint8_t> &Data
) : Binary(Data
) {}
154 std::vector
<uint8_t> denormalize(IO
&) {
155 std::vector
<uint8_t> Data
;
157 raw_string_ostream
OSS(Str
);
158 Binary
.writeAsBinary(OSS
);
160 Data
.assign(Str
.begin(), Str
.end());
167 static void mapping(IO
&Io
, exegesis::InstructionBenchmark
&Obj
) {
168 Io
.mapRequired("mode", Obj
.Mode
);
169 Io
.mapRequired("key", Obj
.Key
);
170 Io
.mapRequired("cpu_name", Obj
.CpuName
);
171 Io
.mapRequired("llvm_triple", Obj
.LLVMTriple
);
172 Io
.mapRequired("num_repetitions", Obj
.NumRepetitions
);
173 Io
.mapRequired("measurements", Obj
.Measurements
);
174 Io
.mapRequired("error", Obj
.Error
);
175 Io
.mapOptional("info", Obj
.Info
);
177 MappingNormalization
<NormalizedBinary
, std::vector
<uint8_t>> BinaryString(
178 Io
, Obj
.AssembledSnippet
);
179 Io
.mapOptional("assembled_snippet", BinaryString
->Binary
);
186 LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(exegesis::InstructionBenchmark
)
190 void BenchmarkResultContext::addRegEntry(unsigned RegNo
, llvm::StringRef Name
) {
191 assert(RegNoToName
.find(RegNo
) == RegNoToName
.end());
192 assert(RegNameToNo
.find(Name
) == RegNameToNo
.end());
193 RegNoToName
[RegNo
] = Name
;
194 RegNameToNo
[Name
] = RegNo
;
197 llvm::StringRef
BenchmarkResultContext::getRegName(unsigned RegNo
) const {
198 const auto Itr
= RegNoToName
.find(RegNo
);
199 if (Itr
!= RegNoToName
.end())
204 unsigned BenchmarkResultContext::getRegNo(llvm::StringRef Name
) const {
205 const auto Itr
= RegNameToNo
.find(Name
);
206 if (Itr
!= RegNameToNo
.end())
211 void BenchmarkResultContext::addInstrEntry(unsigned Opcode
,
212 llvm::StringRef Name
) {
213 assert(InstrOpcodeToName
.find(Opcode
) == InstrOpcodeToName
.end());
214 assert(InstrNameToOpcode
.find(Name
) == InstrNameToOpcode
.end());
215 InstrOpcodeToName
[Opcode
] = Name
;
216 InstrNameToOpcode
[Name
] = Opcode
;
219 llvm::StringRef
BenchmarkResultContext::getInstrName(unsigned Opcode
) const {
220 const auto Itr
= InstrOpcodeToName
.find(Opcode
);
221 if (Itr
!= InstrOpcodeToName
.end())
226 unsigned BenchmarkResultContext::getInstrOpcode(llvm::StringRef Name
) const {
227 const auto Itr
= InstrNameToOpcode
.find(Name
);
228 if (Itr
!= InstrNameToOpcode
.end())
233 template <typename ObjectOrList
>
234 static llvm::Expected
<ObjectOrList
>
235 readYamlCommon(const BenchmarkResultContext
&Context
,
236 llvm::StringRef Filename
) {
237 if (auto ExpectedMemoryBuffer
=
238 llvm::errorOrToExpected(llvm::MemoryBuffer::getFile(Filename
))) {
239 std::unique_ptr
<llvm::MemoryBuffer
> MemoryBuffer
=
240 std::move(ExpectedMemoryBuffer
.get());
241 llvm::yaml::Input
Yin(*MemoryBuffer
, getUntypedContext(Context
));
242 ObjectOrList Benchmark
;
246 return ExpectedMemoryBuffer
.takeError();
250 llvm::Expected
<InstructionBenchmark
>
251 InstructionBenchmark::readYaml(const BenchmarkResultContext
&Context
,
252 llvm::StringRef Filename
) {
253 return readYamlCommon
<InstructionBenchmark
>(Context
, Filename
);
256 llvm::Expected
<std::vector
<InstructionBenchmark
>>
257 InstructionBenchmark::readYamls(const BenchmarkResultContext
&Context
,
258 llvm::StringRef Filename
) {
259 return readYamlCommon
<std::vector
<InstructionBenchmark
>>(Context
, Filename
);
262 void InstructionBenchmark::writeYamlTo(const BenchmarkResultContext
&Context
,
263 llvm::raw_ostream
&OS
) {
264 llvm::yaml::Output
Yout(OS
, getUntypedContext(Context
));
268 void InstructionBenchmark::readYamlFrom(const BenchmarkResultContext
&Context
,
269 llvm::StringRef InputContent
) {
270 llvm::yaml::Input
Yin(InputContent
, getUntypedContext(Context
));
275 InstructionBenchmark::writeYaml(const BenchmarkResultContext
&Context
,
276 const llvm::StringRef Filename
) {
277 if (Filename
== "-") {
278 writeYamlTo(Context
, llvm::outs());
281 if (auto E
= llvm::errorCodeToError(
282 openFileForWrite(Filename
, ResultFD
, llvm::sys::fs::CD_CreateAlways
,
283 llvm::sys::fs::F_Text
))) {
286 llvm::raw_fd_ostream
Ostr(ResultFD
, true /*shouldClose*/);
287 writeYamlTo(Context
, Ostr
);
289 return llvm::Error::success();
292 void BenchmarkMeasureStats::push(const BenchmarkMeasure
&BM
) {
295 assert(Key
== BM
.Key
);
297 SumValues
+= BM
.Value
;
298 MaxValue
= std::max(MaxValue
, BM
.Value
);
299 MinValue
= std::min(MinValue
, BM
.Value
);
302 } // namespace exegesis