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