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
26namespace llvm {
27namespace mca {
28
29// This virtual dtor serves as the anchor for the CodeRegionGenerator class.
30CodeRegionGenerator::~CodeRegionGenerator() {}
31
32// This class provides the callbacks that occur when parsing input assembly.
33class MCStreamerWrapper final : public MCStreamer {
34  CodeRegions &Regions;
35
36public:
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
66Expected<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
111void 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
146void 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