xref: /llvm-project/llvm/tools/llvm-mca/CodeRegionGenerator.cpp (revision 1de0535e84f03941badc8021bbc87a8c674a379f)
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/MCTargetOptions.h"
21 #include "llvm/Support/Error.h"
22 #include "llvm/Support/SMLoc.h"
23 #include <memory>
24 
25 namespace llvm {
26 namespace mca {
27 
28 // This virtual dtor serves as the anchor for the CodeRegionGenerator class.
29 CodeRegionGenerator::~CodeRegionGenerator() {}
30 
31 Expected<const CodeRegions &> AsmCodeRegionGenerator::parseCodeRegions(
32     const std::unique_ptr<MCInstPrinter> &IP, bool SkipFailures) {
33   MCTargetOptions Opts;
34   Opts.PreserveAsmComments = false;
35   CodeRegions &Regions = getRegions();
36   MCStreamerWrapper *Str = getMCStreamer();
37 
38   // Need to initialize an MCTargetStreamer otherwise
39   // certain asm directives will cause a segfault.
40   // Using nulls() so that anything emitted by the MCTargetStreamer
41   // doesn't show up in the llvm-mca output.
42   raw_ostream &OSRef = nulls();
43   formatted_raw_ostream FOSRef(OSRef);
44   TheTarget.createAsmTargetStreamer(*Str, FOSRef, IP.get(),
45                                     /*IsVerboseAsm=*/true);
46 
47   // Create a MCAsmParser and setup the lexer to recognize llvm-mca ASM
48   // comments.
49   std::unique_ptr<MCAsmParser> Parser(
50       createMCAsmParser(Regions.getSourceMgr(), Ctx, *Str, MAI));
51   MCAsmLexer &Lexer = Parser->getLexer();
52   MCACommentConsumer *CCP = getCommentConsumer();
53   Lexer.setCommentConsumer(CCP);
54   // Enable support for MASM literal numbers (example: 05h, 101b).
55   Lexer.setLexMasmIntegers(true);
56 
57   std::unique_ptr<MCTargetAsmParser> TAP(
58       TheTarget.createMCAsmParser(STI, *Parser, MCII, Opts));
59   if (!TAP)
60     return make_error<StringError>(
61         "This target does not support assembly parsing.",
62         inconvertibleErrorCode());
63   Parser->setTargetParser(*TAP);
64   // Parser->Run() confusingly returns true on errors, in which case the errors
65   // were already shown to the user. SkipFailures implies continuing in the
66   // presence of any kind of failure within the parser, in which case failing
67   // input lines are not represented, but the rest of the input remains.
68   if (Parser->Run(false) && !SkipFailures) {
69     const char *Message = "Assembly input parsing had errors, use "
70                           "-skip-unsupported-instructions=parse-failure "
71                           "to drop failing lines from the input.";
72     return make_error<StringError>(Message, inconvertibleErrorCode());
73   }
74 
75   if (CCP->hadErr())
76     return make_error<StringError>("There was an error parsing comments.",
77                                    inconvertibleErrorCode());
78 
79   // Set the assembler dialect from the input. llvm-mca will use this as the
80   // default dialect when printing reports.
81   AssemblerDialect = Parser->getAssemblerDialect();
82   return Regions;
83 }
84 
85 void AnalysisRegionCommentConsumer::HandleComment(SMLoc Loc,
86                                                   StringRef CommentText) {
87   // Skip empty comments.
88   StringRef Comment(CommentText);
89   if (Comment.empty())
90     return;
91 
92   // Skip spaces and tabs.
93   unsigned Position = Comment.find_first_not_of(" \t");
94   if (Position >= Comment.size())
95     // We reached the end of the comment. Bail out.
96     return;
97 
98   Comment = Comment.drop_front(Position);
99   if (Comment.consume_front("LLVM-MCA-END")) {
100     // Skip spaces and tabs.
101     Position = Comment.find_first_not_of(" \t");
102     if (Position < Comment.size())
103       Comment = Comment.drop_front(Position);
104     Regions.endRegion(Comment, Loc);
105     return;
106   }
107 
108   // Try to parse the LLVM-MCA-BEGIN comment.
109   if (!Comment.consume_front("LLVM-MCA-BEGIN"))
110     return;
111 
112   // Skip spaces and tabs.
113   Position = Comment.find_first_not_of(" \t");
114   if (Position < Comment.size())
115     Comment = Comment.drop_front(Position);
116   // Use the rest of the string as a descriptor for this code snippet.
117   Regions.beginRegion(Comment, Loc);
118 }
119 
120 void InstrumentRegionCommentConsumer::HandleComment(SMLoc Loc,
121                                                     StringRef CommentText) {
122   // Skip empty comments.
123   StringRef Comment(CommentText);
124   if (Comment.empty())
125     return;
126 
127   // Skip spaces and tabs.
128   unsigned Position = Comment.find_first_not_of(" \t");
129   if (Position >= Comment.size())
130     // We reached the end of the comment. Bail out.
131     return;
132   Comment = Comment.drop_front(Position);
133 
134   // Bail out if not an MCA style comment
135   if (!Comment.consume_front("LLVM-MCA-"))
136     return;
137 
138   // Skip AnalysisRegion comments
139   if (Comment.consume_front("BEGIN") || Comment.consume_front("END"))
140     return;
141 
142   if (IM.shouldIgnoreInstruments())
143     return;
144 
145   auto [InstrumentKind, Data] = Comment.split(" ");
146 
147   // An error if not of the form LLVM-MCA-TARGET-KIND
148   if (!IM.supportsInstrumentType(InstrumentKind)) {
149     if (InstrumentKind.empty())
150       SM.PrintMessage(
151           Loc, llvm::SourceMgr::DK_Error,
152           "No instrumentation kind was provided in LLVM-MCA comment");
153     else
154       SM.PrintMessage(Loc, llvm::SourceMgr::DK_Error,
155                       "Unknown instrumentation type in LLVM-MCA comment: " +
156                           InstrumentKind);
157     FoundError = true;
158     return;
159   }
160 
161   UniqueInstrument I = IM.createInstrument(InstrumentKind, Data);
162   if (!I) {
163     if (Data.empty())
164       SM.PrintMessage(Loc, llvm::SourceMgr::DK_Error,
165                       "Failed to create " + InstrumentKind +
166                           " instrument with no data");
167     else
168       SM.PrintMessage(Loc, llvm::SourceMgr::DK_Error,
169                       "Failed to create " + InstrumentKind +
170                           " instrument with data: " + Data);
171     FoundError = true;
172     return;
173   }
174 
175   // End InstrumentType region if one is open
176   if (Regions.isRegionActive(InstrumentKind))
177     Regions.endRegion(InstrumentKind, Loc);
178   // Start new instrumentation region
179   Regions.beginRegion(InstrumentKind, Loc, std::move(I));
180 }
181 
182 } // namespace mca
183 } // namespace llvm
184