BenchmarkResult.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. //===-- BenchmarkResult.cpp -------------------------------------*- C++ -*-===//
  2. //
  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
  6. //
  7. //===----------------------------------------------------------------------===//
  8. #include "BenchmarkResult.h"
  9. #include "BenchmarkRunner.h"
  10. #include "Error.h"
  11. #include "llvm/ADT/STLExtras.h"
  12. #include "llvm/ADT/ScopeExit.h"
  13. #include "llvm/ADT/StringMap.h"
  14. #include "llvm/ADT/StringRef.h"
  15. #include "llvm/ADT/bit.h"
  16. #include "llvm/ObjectYAML/YAML.h"
  17. #include "llvm/Support/FileOutputBuffer.h"
  18. #include "llvm/Support/FileSystem.h"
  19. #include "llvm/Support/Format.h"
  20. #include "llvm/Support/raw_ostream.h"
  21. static constexpr const char kIntegerPrefix[] = "i_0x";
  22. static constexpr const char kDoublePrefix[] = "f_";
  23. static constexpr const char kInvalidOperand[] = "INVALID";
  24. namespace llvm {
  25. namespace {
  26. // A mutable struct holding an LLVMState that can be passed through the
  27. // serialization process to encode/decode registers and instructions.
  28. struct YamlContext {
  29. YamlContext(const exegesis::LLVMState &State)
  30. : State(&State), ErrorStream(LastError),
  31. OpcodeNameToOpcodeIdx(State.getOpcodeNameToOpcodeIdxMapping()),
  32. RegNameToRegNo(State.getRegNameToRegNoMapping()) {}
  33. void serializeMCInst(const MCInst &MCInst, raw_ostream &OS) {
  34. OS << getInstrName(MCInst.getOpcode());
  35. for (const auto &Op : MCInst) {
  36. OS << ' ';
  37. serializeMCOperand(Op, OS);
  38. }
  39. }
  40. void deserializeMCInst(StringRef String, MCInst &Value) {
  41. SmallVector<StringRef, 16> Pieces;
  42. String.split(Pieces, " ", /* MaxSplit */ -1, /* KeepEmpty */ false);
  43. if (Pieces.empty()) {
  44. ErrorStream << "Unknown Instruction: '" << String << "'\n";
  45. return;
  46. }
  47. bool ProcessOpcode = true;
  48. for (StringRef Piece : Pieces) {
  49. if (ProcessOpcode)
  50. Value.setOpcode(getInstrOpcode(Piece));
  51. else
  52. Value.addOperand(deserializeMCOperand(Piece));
  53. ProcessOpcode = false;
  54. }
  55. }
  56. std::string &getLastError() { return ErrorStream.str(); }
  57. raw_string_ostream &getErrorStream() { return ErrorStream; }
  58. StringRef getRegName(unsigned RegNo) {
  59. // Special case: RegNo 0 is NoRegister. We have to deal with it explicitly.
  60. if (RegNo == 0)
  61. return kNoRegister;
  62. const StringRef RegName = State->getRegInfo().getName(RegNo);
  63. if (RegName.empty())
  64. ErrorStream << "No register with enum value '" << RegNo << "'\n";
  65. return RegName;
  66. }
  67. std::optional<unsigned> getRegNo(StringRef RegName) {
  68. auto Iter = RegNameToRegNo.find(RegName);
  69. if (Iter != RegNameToRegNo.end())
  70. return Iter->second;
  71. ErrorStream << "No register with name '" << RegName << "'\n";
  72. return std::nullopt;
  73. }
  74. private:
  75. void serializeIntegerOperand(raw_ostream &OS, int64_t Value) {
  76. OS << kIntegerPrefix;
  77. OS.write_hex(bit_cast<uint64_t>(Value));
  78. }
  79. bool tryDeserializeIntegerOperand(StringRef String, int64_t &Value) {
  80. if (!String.consume_front(kIntegerPrefix))
  81. return false;
  82. return !String.consumeInteger(16, Value);
  83. }
  84. void serializeFPOperand(raw_ostream &OS, double Value) {
  85. OS << kDoublePrefix << format("%la", Value);
  86. }
  87. bool tryDeserializeFPOperand(StringRef String, double &Value) {
  88. if (!String.consume_front(kDoublePrefix))
  89. return false;
  90. char *EndPointer = nullptr;
  91. Value = strtod(String.begin(), &EndPointer);
  92. return EndPointer == String.end();
  93. }
  94. void serializeMCOperand(const MCOperand &MCOperand, raw_ostream &OS) {
  95. if (MCOperand.isReg()) {
  96. OS << getRegName(MCOperand.getReg());
  97. } else if (MCOperand.isImm()) {
  98. serializeIntegerOperand(OS, MCOperand.getImm());
  99. } else if (MCOperand.isDFPImm()) {
  100. serializeFPOperand(OS, bit_cast<double>(MCOperand.getDFPImm()));
  101. } else {
  102. OS << kInvalidOperand;
  103. }
  104. }
  105. MCOperand deserializeMCOperand(StringRef String) {
  106. assert(!String.empty());
  107. int64_t IntValue = 0;
  108. double DoubleValue = 0;
  109. if (tryDeserializeIntegerOperand(String, IntValue))
  110. return MCOperand::createImm(IntValue);
  111. if (tryDeserializeFPOperand(String, DoubleValue))
  112. return MCOperand::createDFPImm(bit_cast<uint64_t>(DoubleValue));
  113. if (auto RegNo = getRegNo(String))
  114. return MCOperand::createReg(*RegNo);
  115. if (String != kInvalidOperand)
  116. ErrorStream << "Unknown Operand: '" << String << "'\n";
  117. return {};
  118. }
  119. StringRef getInstrName(unsigned InstrNo) {
  120. const StringRef InstrName = State->getInstrInfo().getName(InstrNo);
  121. if (InstrName.empty())
  122. ErrorStream << "No opcode with enum value '" << InstrNo << "'\n";
  123. return InstrName;
  124. }
  125. unsigned getInstrOpcode(StringRef InstrName) {
  126. auto Iter = OpcodeNameToOpcodeIdx.find(InstrName);
  127. if (Iter != OpcodeNameToOpcodeIdx.end())
  128. return Iter->second;
  129. ErrorStream << "No opcode with name '" << InstrName << "'\n";
  130. return 0;
  131. }
  132. const exegesis::LLVMState *State;
  133. std::string LastError;
  134. raw_string_ostream ErrorStream;
  135. const DenseMap<StringRef, unsigned> &OpcodeNameToOpcodeIdx;
  136. const DenseMap<StringRef, unsigned> &RegNameToRegNo;
  137. };
  138. } // namespace
  139. // Defining YAML traits for IO.
  140. namespace yaml {
  141. static YamlContext &getTypedContext(void *Ctx) {
  142. return *reinterpret_cast<YamlContext *>(Ctx);
  143. }
  144. // std::vector<MCInst> will be rendered as a list.
  145. template <> struct SequenceElementTraits<MCInst> {
  146. static const bool flow = false;
  147. };
  148. template <> struct ScalarTraits<MCInst> {
  149. static void output(const MCInst &Value, void *Ctx, raw_ostream &Out) {
  150. getTypedContext(Ctx).serializeMCInst(Value, Out);
  151. }
  152. static StringRef input(StringRef Scalar, void *Ctx, MCInst &Value) {
  153. YamlContext &Context = getTypedContext(Ctx);
  154. Context.deserializeMCInst(Scalar, Value);
  155. return Context.getLastError();
  156. }
  157. // By default strings are quoted only when necessary.
  158. // We force the use of single quotes for uniformity.
  159. static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
  160. static const bool flow = true;
  161. };
  162. // std::vector<exegesis::Measure> will be rendered as a list.
  163. template <> struct SequenceElementTraits<exegesis::BenchmarkMeasure> {
  164. static const bool flow = false;
  165. };
  166. // exegesis::Measure is rendererd as a flow instead of a list.
  167. // e.g. { "key": "the key", "value": 0123 }
  168. template <> struct MappingTraits<exegesis::BenchmarkMeasure> {
  169. static void mapping(IO &Io, exegesis::BenchmarkMeasure &Obj) {
  170. Io.mapRequired("key", Obj.Key);
  171. if (!Io.outputting()) {
  172. // For backward compatibility, interpret debug_string as a key.
  173. Io.mapOptional("debug_string", Obj.Key);
  174. }
  175. Io.mapRequired("value", Obj.PerInstructionValue);
  176. Io.mapOptional("per_snippet_value", Obj.PerSnippetValue);
  177. }
  178. static const bool flow = true;
  179. };
  180. template <>
  181. struct ScalarEnumerationTraits<exegesis::InstructionBenchmark::ModeE> {
  182. static void enumeration(IO &Io,
  183. exegesis::InstructionBenchmark::ModeE &Value) {
  184. Io.enumCase(Value, "", exegesis::InstructionBenchmark::Unknown);
  185. Io.enumCase(Value, "latency", exegesis::InstructionBenchmark::Latency);
  186. Io.enumCase(Value, "uops", exegesis::InstructionBenchmark::Uops);
  187. Io.enumCase(Value, "inverse_throughput",
  188. exegesis::InstructionBenchmark::InverseThroughput);
  189. }
  190. };
  191. // std::vector<exegesis::RegisterValue> will be rendered as a list.
  192. template <> struct SequenceElementTraits<exegesis::RegisterValue> {
  193. static const bool flow = false;
  194. };
  195. template <> struct ScalarTraits<exegesis::RegisterValue> {
  196. static constexpr const unsigned kRadix = 16;
  197. static constexpr const bool kSigned = false;
  198. static void output(const exegesis::RegisterValue &RV, void *Ctx,
  199. raw_ostream &Out) {
  200. YamlContext &Context = getTypedContext(Ctx);
  201. Out << Context.getRegName(RV.Register) << "=0x"
  202. << toString(RV.Value, kRadix, kSigned);
  203. }
  204. static StringRef input(StringRef String, void *Ctx,
  205. exegesis::RegisterValue &RV) {
  206. SmallVector<StringRef, 2> Pieces;
  207. String.split(Pieces, "=0x", /* MaxSplit */ -1,
  208. /* KeepEmpty */ false);
  209. YamlContext &Context = getTypedContext(Ctx);
  210. std::optional<unsigned> RegNo;
  211. if (Pieces.size() == 2 && (RegNo = Context.getRegNo(Pieces[0]))) {
  212. RV.Register = *RegNo;
  213. const unsigned BitsNeeded = APInt::getBitsNeeded(Pieces[1], kRadix);
  214. RV.Value = APInt(BitsNeeded, Pieces[1], kRadix);
  215. } else {
  216. Context.getErrorStream()
  217. << "Unknown initial register value: '" << String << "'";
  218. }
  219. return Context.getLastError();
  220. }
  221. static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
  222. static const bool flow = true;
  223. };
  224. template <>
  225. struct MappingContextTraits<exegesis::InstructionBenchmarkKey, YamlContext> {
  226. static void mapping(IO &Io, exegesis::InstructionBenchmarkKey &Obj,
  227. YamlContext &Context) {
  228. Io.setContext(&Context);
  229. Io.mapRequired("instructions", Obj.Instructions);
  230. Io.mapOptional("config", Obj.Config);
  231. Io.mapRequired("register_initial_values", Obj.RegisterInitialValues);
  232. }
  233. };
  234. template <>
  235. struct MappingContextTraits<exegesis::InstructionBenchmark, YamlContext> {
  236. struct NormalizedBinary {
  237. NormalizedBinary(IO &io) {}
  238. NormalizedBinary(IO &, std::vector<uint8_t> &Data) : Binary(Data) {}
  239. std::vector<uint8_t> denormalize(IO &) {
  240. std::vector<uint8_t> Data;
  241. std::string Str;
  242. raw_string_ostream OSS(Str);
  243. Binary.writeAsBinary(OSS);
  244. OSS.flush();
  245. Data.assign(Str.begin(), Str.end());
  246. return Data;
  247. }
  248. BinaryRef Binary;
  249. };
  250. static void mapping(IO &Io, exegesis::InstructionBenchmark &Obj,
  251. YamlContext &Context) {
  252. Io.mapRequired("mode", Obj.Mode);
  253. Io.mapRequired("key", Obj.Key, Context);
  254. Io.mapRequired("cpu_name", Obj.CpuName);
  255. Io.mapRequired("llvm_triple", Obj.LLVMTriple);
  256. Io.mapRequired("num_repetitions", Obj.NumRepetitions);
  257. Io.mapRequired("measurements", Obj.Measurements);
  258. Io.mapRequired("error", Obj.Error);
  259. Io.mapOptional("info", Obj.Info);
  260. // AssembledSnippet
  261. MappingNormalization<NormalizedBinary, std::vector<uint8_t>> BinaryString(
  262. Io, Obj.AssembledSnippet);
  263. Io.mapOptional("assembled_snippet", BinaryString->Binary);
  264. }
  265. };
  266. template <> struct MappingTraits<exegesis::InstructionBenchmark::TripleAndCpu> {
  267. static void mapping(IO &Io,
  268. exegesis::InstructionBenchmark::TripleAndCpu &Obj) {
  269. assert(!Io.outputting() && "can only read TripleAndCpu");
  270. // Read triple.
  271. Io.mapRequired("llvm_triple", Obj.LLVMTriple);
  272. Io.mapRequired("cpu_name", Obj.CpuName);
  273. // Drop everything else.
  274. }
  275. };
  276. } // namespace yaml
  277. namespace exegesis {
  278. Expected<std::set<InstructionBenchmark::TripleAndCpu>>
  279. InstructionBenchmark::readTriplesAndCpusFromYamls(MemoryBufferRef Buffer) {
  280. // We're only mapping a field, drop other fields and silence the corresponding
  281. // warnings.
  282. yaml::Input Yin(
  283. Buffer, nullptr, +[](const SMDiagnostic &, void *Context) {});
  284. Yin.setAllowUnknownKeys(true);
  285. std::set<TripleAndCpu> Result;
  286. yaml::EmptyContext Context;
  287. while (Yin.setCurrentDocument()) {
  288. TripleAndCpu TC;
  289. yamlize(Yin, TC, /*unused*/ true, Context);
  290. if (Yin.error())
  291. return errorCodeToError(Yin.error());
  292. Result.insert(TC);
  293. Yin.nextDocument();
  294. }
  295. return Result;
  296. }
  297. Expected<InstructionBenchmark>
  298. InstructionBenchmark::readYaml(const LLVMState &State, MemoryBufferRef Buffer) {
  299. yaml::Input Yin(Buffer);
  300. YamlContext Context(State);
  301. InstructionBenchmark Benchmark;
  302. if (Yin.setCurrentDocument())
  303. yaml::yamlize(Yin, Benchmark, /*unused*/ true, Context);
  304. if (!Context.getLastError().empty())
  305. return make_error<Failure>(Context.getLastError());
  306. return std::move(Benchmark);
  307. }
  308. Expected<std::vector<InstructionBenchmark>>
  309. InstructionBenchmark::readYamls(const LLVMState &State,
  310. MemoryBufferRef Buffer) {
  311. yaml::Input Yin(Buffer);
  312. YamlContext Context(State);
  313. std::vector<InstructionBenchmark> Benchmarks;
  314. while (Yin.setCurrentDocument()) {
  315. Benchmarks.emplace_back();
  316. yamlize(Yin, Benchmarks.back(), /*unused*/ true, Context);
  317. if (Yin.error())
  318. return errorCodeToError(Yin.error());
  319. if (!Context.getLastError().empty())
  320. return make_error<Failure>(Context.getLastError());
  321. Yin.nextDocument();
  322. }
  323. return std::move(Benchmarks);
  324. }
  325. Error InstructionBenchmark::writeYamlTo(const LLVMState &State,
  326. raw_ostream &OS) {
  327. auto Cleanup = make_scope_exit([&] { OS.flush(); });
  328. yaml::Output Yout(OS, nullptr /*Ctx*/, 200 /*WrapColumn*/);
  329. YamlContext Context(State);
  330. Yout.beginDocuments();
  331. yaml::yamlize(Yout, *this, /*unused*/ true, Context);
  332. if (!Context.getLastError().empty())
  333. return make_error<Failure>(Context.getLastError());
  334. Yout.endDocuments();
  335. return Error::success();
  336. }
  337. Error InstructionBenchmark::readYamlFrom(const LLVMState &State,
  338. StringRef InputContent) {
  339. yaml::Input Yin(InputContent);
  340. YamlContext Context(State);
  341. if (Yin.setCurrentDocument())
  342. yaml::yamlize(Yin, *this, /*unused*/ true, Context);
  343. if (!Context.getLastError().empty())
  344. return make_error<Failure>(Context.getLastError());
  345. return Error::success();
  346. }
  347. void PerInstructionStats::push(const BenchmarkMeasure &BM) {
  348. if (Key.empty())
  349. Key = BM.Key;
  350. assert(Key == BM.Key);
  351. ++NumValues;
  352. SumValues += BM.PerInstructionValue;
  353. MaxValue = std::max(MaxValue, BM.PerInstructionValue);
  354. MinValue = std::min(MinValue, BM.PerInstructionValue);
  355. }
  356. bool operator==(const BenchmarkMeasure &A, const BenchmarkMeasure &B) {
  357. return std::tie(A.Key, A.PerInstructionValue, A.PerSnippetValue) ==
  358. std::tie(B.Key, B.PerInstructionValue, B.PerSnippetValue);
  359. }
  360. } // namespace exegesis
  361. } // namespace llvm