CodeRegionGenerator.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. //===----------------------- CodeRegionGenerator.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. /// \file
  9. ///
  10. /// This file defines classes responsible for generating llvm-mca
  11. /// CodeRegions from various types of input. llvm-mca only analyzes CodeRegions,
  12. /// so the classes here provide the input-to-CodeRegions translation.
  13. //
  14. //===----------------------------------------------------------------------===//
  15. #include "CodeRegionGenerator.h"
  16. #include "llvm/ADT/ArrayRef.h"
  17. #include "llvm/ADT/StringRef.h"
  18. #include "llvm/MC/MCParser/MCTargetAsmParser.h"
  19. #include "llvm/MC/MCStreamer.h"
  20. #include "llvm/MC/MCTargetOptions.h"
  21. #include "llvm/Support/Error.h"
  22. #include "llvm/Support/SMLoc.h"
  23. #include <memory>
  24. namespace llvm {
  25. namespace mca {
  26. // This virtual dtor serves as the anchor for the CodeRegionGenerator class.
  27. CodeRegionGenerator::~CodeRegionGenerator() {}
  28. // This class provides the callbacks that occur when parsing input assembly.
  29. class MCStreamerWrapper final : public MCStreamer {
  30. CodeRegions &Regions;
  31. public:
  32. MCStreamerWrapper(MCContext &Context, mca::CodeRegions &R)
  33. : MCStreamer(Context), Regions(R) {}
  34. // We only want to intercept the emission of new instructions.
  35. void emitInstruction(const MCInst &Inst,
  36. const MCSubtargetInfo & /* unused */) override {
  37. Regions.addInstruction(Inst);
  38. }
  39. bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override {
  40. return true;
  41. }
  42. void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size,
  43. Align ByteAlignment) override {}
  44. void emitZerofill(MCSection *Section, MCSymbol *Symbol = nullptr,
  45. uint64_t Size = 0, Align ByteAlignment = Align(1),
  46. SMLoc Loc = SMLoc()) override {}
  47. void emitGPRel32Value(const MCExpr *Value) override {}
  48. void beginCOFFSymbolDef(const MCSymbol *Symbol) override {}
  49. void emitCOFFSymbolStorageClass(int StorageClass) override {}
  50. void emitCOFFSymbolType(int Type) override {}
  51. void endCOFFSymbolDef() override {}
  52. ArrayRef<MCInst> GetInstructionSequence(unsigned Index) const {
  53. return Regions.getInstructionSequence(Index);
  54. }
  55. };
  56. Expected<const CodeRegions &> AsmCodeRegionGenerator::parseCodeRegions(
  57. const std::unique_ptr<MCInstPrinter> &IP) {
  58. MCTargetOptions Opts;
  59. Opts.PreserveAsmComments = false;
  60. CodeRegions &Regions = getRegions();
  61. MCStreamerWrapper Str(Ctx, Regions);
  62. // Need to initialize an MCTargetStreamer otherwise
  63. // certain asm directives will cause a segfault.
  64. // Using nulls() so that anything emitted by the MCTargetStreamer
  65. // doesn't show up in the llvm-mca output.
  66. raw_ostream &OSRef = nulls();
  67. formatted_raw_ostream FOSRef(OSRef);
  68. TheTarget.createAsmTargetStreamer(Str, FOSRef, IP.get(),
  69. /*IsVerboseAsm=*/true);
  70. // Create a MCAsmParser and setup the lexer to recognize llvm-mca ASM
  71. // comments.
  72. std::unique_ptr<MCAsmParser> Parser(
  73. createMCAsmParser(Regions.getSourceMgr(), Ctx, Str, MAI));
  74. MCAsmLexer &Lexer = Parser->getLexer();
  75. MCACommentConsumer *CCP = getCommentConsumer();
  76. Lexer.setCommentConsumer(CCP);
  77. // Enable support for MASM literal numbers (example: 05h, 101b).
  78. Lexer.setLexMasmIntegers(true);
  79. std::unique_ptr<MCTargetAsmParser> TAP(
  80. TheTarget.createMCAsmParser(STI, *Parser, MCII, Opts));
  81. if (!TAP)
  82. return make_error<StringError>(
  83. "This target does not support assembly parsing.",
  84. inconvertibleErrorCode());
  85. Parser->setTargetParser(*TAP);
  86. Parser->Run(false);
  87. if (CCP->hadErr())
  88. return make_error<StringError>("There was an error parsing comments.",
  89. inconvertibleErrorCode());
  90. // Set the assembler dialect from the input. llvm-mca will use this as the
  91. // default dialect when printing reports.
  92. AssemblerDialect = Parser->getAssemblerDialect();
  93. return Regions;
  94. }
  95. void AnalysisRegionCommentConsumer::HandleComment(SMLoc Loc,
  96. StringRef CommentText) {
  97. // Skip empty comments.
  98. StringRef Comment(CommentText);
  99. if (Comment.empty())
  100. return;
  101. // Skip spaces and tabs.
  102. unsigned Position = Comment.find_first_not_of(" \t");
  103. if (Position >= Comment.size())
  104. // We reached the end of the comment. Bail out.
  105. return;
  106. Comment = Comment.drop_front(Position);
  107. if (Comment.consume_front("LLVM-MCA-END")) {
  108. // Skip spaces and tabs.
  109. Position = Comment.find_first_not_of(" \t");
  110. if (Position < Comment.size())
  111. Comment = Comment.drop_front(Position);
  112. Regions.endRegion(Comment, Loc);
  113. return;
  114. }
  115. // Try to parse the LLVM-MCA-BEGIN comment.
  116. if (!Comment.consume_front("LLVM-MCA-BEGIN"))
  117. return;
  118. // Skip spaces and tabs.
  119. Position = Comment.find_first_not_of(" \t");
  120. if (Position < Comment.size())
  121. Comment = Comment.drop_front(Position);
  122. // Use the rest of the string as a descriptor for this code snippet.
  123. Regions.beginRegion(Comment, Loc);
  124. }
  125. void InstrumentRegionCommentConsumer::HandleComment(SMLoc Loc,
  126. StringRef CommentText) {
  127. // Skip empty comments.
  128. StringRef Comment(CommentText);
  129. if (Comment.empty())
  130. return;
  131. // Skip spaces and tabs.
  132. unsigned Position = Comment.find_first_not_of(" \t");
  133. if (Position >= Comment.size())
  134. // We reached the end of the comment. Bail out.
  135. return;
  136. Comment = Comment.drop_front(Position);
  137. // Bail out if not an MCA style comment
  138. if (!Comment.consume_front("LLVM-MCA-"))
  139. return;
  140. // Skip AnalysisRegion comments
  141. if (Comment.consume_front("BEGIN") || Comment.consume_front("END"))
  142. return;
  143. if (IM.shouldIgnoreInstruments())
  144. return;
  145. auto [InstrumentKind, Data] = Comment.split(" ");
  146. // An error if not of the form LLVM-MCA-TARGET-KIND
  147. if (!IM.supportsInstrumentType(InstrumentKind)) {
  148. if (InstrumentKind.empty())
  149. SM.PrintMessage(
  150. Loc, llvm::SourceMgr::DK_Error,
  151. "No instrumentation kind was provided in LLVM-MCA comment");
  152. else
  153. SM.PrintMessage(Loc, llvm::SourceMgr::DK_Error,
  154. "Unknown instrumentation type in LLVM-MCA comment: " +
  155. InstrumentKind);
  156. FoundError = true;
  157. return;
  158. }
  159. SharedInstrument I = IM.createInstrument(InstrumentKind, Data);
  160. if (!I) {
  161. if (Data.empty())
  162. SM.PrintMessage(Loc, llvm::SourceMgr::DK_Error,
  163. "Failed to create " + InstrumentKind +
  164. " instrument with no data");
  165. else
  166. SM.PrintMessage(Loc, llvm::SourceMgr::DK_Error,
  167. "Failed to create " + InstrumentKind +
  168. " instrument with data: " + Data);
  169. FoundError = true;
  170. return;
  171. }
  172. // End InstrumentType region if one is open
  173. if (Regions.isRegionActive(InstrumentKind))
  174. Regions.endRegion(InstrumentKind, Loc);
  175. // Start new instrumentation region
  176. Regions.beginRegion(InstrumentKind, Loc, I);
  177. }
  178. } // namespace mca
  179. } // namespace llvm