xref: /freebsd/contrib/llvm-project/llvm/tools/llvm-mca/CodeRegionGenerator.cpp (revision 5036d9652a5701d00e9e40ea942c278e9f77d33d)
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 
46   // Create a MCAsmParser and setup the lexer to recognize llvm-mca ASM
47   // comments.
48   std::unique_ptr<MCAsmParser> Parser(
49       createMCAsmParser(Regions.getSourceMgr(), Ctx, *Str, MAI));
50   MCAsmLexer &Lexer = Parser->getLexer();
51   MCACommentConsumer *CCP = getCommentConsumer();
52   Lexer.setCommentConsumer(CCP);
53   // Enable support for MASM literal numbers (example: 05h, 101b).
54   Lexer.setLexMasmIntegers(true);
55 
56   std::unique_ptr<MCTargetAsmParser> TAP(
57       TheTarget.createMCAsmParser(STI, *Parser, MCII, Opts));
58   if (!TAP)
59     return make_error<StringError>(
60         "This target does not support assembly parsing.",
61         inconvertibleErrorCode());
62   Parser->setTargetParser(*TAP);
63   // Parser->Run() confusingly returns true on errors, in which case the errors
64   // were already shown to the user. SkipFailures implies continuing in the
65   // presence of any kind of failure within the parser, in which case failing
66   // input lines are not represented, but the rest of the input remains.
67   if (Parser->Run(false) && !SkipFailures) {
68     const char *Message = "Assembly input parsing had errors, use "
69                           "-skip-unsupported-instructions=parse-failure "
70                           "to drop failing lines from the input.";
71     return make_error<StringError>(Message, inconvertibleErrorCode());
72   }
73 
74   if (CCP->hadErr())
75     return make_error<StringError>("There was an error parsing comments.",
76                                    inconvertibleErrorCode());
77 
78   // Set the assembler dialect from the input. llvm-mca will use this as the
79   // default dialect when printing reports.
80   AssemblerDialect = Parser->getAssemblerDialect();
81   return Regions;
82 }
83 
84 void AnalysisRegionCommentConsumer::HandleComment(SMLoc Loc,
85                                                   StringRef CommentText) {
86   // Skip empty comments.
87   StringRef Comment(CommentText);
88   if (Comment.empty())
89     return;
90 
91   // Skip spaces and tabs.
92   unsigned Position = Comment.find_first_not_of(" \t");
93   if (Position >= Comment.size())
94     // We reached the end of the comment. Bail out.
95     return;
96 
97   Comment = Comment.drop_front(Position);
98   if (Comment.consume_front("LLVM-MCA-END")) {
99     // Skip spaces and tabs.
100     Position = Comment.find_first_not_of(" \t");
101     if (Position < Comment.size())
102       Comment = Comment.drop_front(Position);
103     Regions.endRegion(Comment, Loc);
104     return;
105   }
106 
107   // Try to parse the LLVM-MCA-BEGIN comment.
108   if (!Comment.consume_front("LLVM-MCA-BEGIN"))
109     return;
110 
111   // Skip spaces and tabs.
112   Position = Comment.find_first_not_of(" \t");
113   if (Position < Comment.size())
114     Comment = Comment.drop_front(Position);
115   // Use the rest of the string as a descriptor for this code snippet.
116   Regions.beginRegion(Comment, Loc);
117 }
118 
119 void InstrumentRegionCommentConsumer::HandleComment(SMLoc Loc,
120                                                     StringRef CommentText) {
121   // Skip empty comments.
122   StringRef Comment(CommentText);
123   if (Comment.empty())
124     return;
125 
126   // Skip spaces and tabs.
127   unsigned Position = Comment.find_first_not_of(" \t");
128   if (Position >= Comment.size())
129     // We reached the end of the comment. Bail out.
130     return;
131   Comment = Comment.drop_front(Position);
132 
133   // Bail out if not an MCA style comment
134   if (!Comment.consume_front("LLVM-MCA-"))
135     return;
136 
137   // Skip AnalysisRegion comments
138   if (Comment.consume_front("BEGIN") || Comment.consume_front("END"))
139     return;
140 
141   if (IM.shouldIgnoreInstruments())
142     return;
143 
144   auto [InstrumentKind, Data] = Comment.split(" ");
145 
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 
160   UniqueInstrument I = IM.createInstrument(InstrumentKind, Data);
161   if (!I) {
162     if (Data.empty())
163       SM.PrintMessage(Loc, llvm::SourceMgr::DK_Error,
164                       "Failed to create " + InstrumentKind +
165                           " instrument with no data");
166     else
167       SM.PrintMessage(Loc, llvm::SourceMgr::DK_Error,
168                       "Failed to create " + InstrumentKind +
169                           " instrument with data: " + Data);
170     FoundError = true;
171     return;
172   }
173 
174   // End InstrumentType region if one is open
175   if (Regions.isRegionActive(InstrumentKind))
176     Regions.endRegion(InstrumentKind, Loc);
177   // Start new instrumentation region
178   Regions.beginRegion(InstrumentKind, Loc, std::move(I));
179 }
180 
181 } // namespace mca
182 } // namespace llvm
183